From 023927f8f3d13ac9e4a2a13c207ad6450f9174ed Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 4 Oct 2013 10:13:26 +0100 Subject: [PATCH 1/6] generator handles request mapping annotation without httpmethod type filter --- .../restDoclet/generation/Generator.java | 185 ++++++++++-------- 1 file changed, 99 insertions(+), 86 deletions(-) diff --git a/src/main/java/org/cloudifysource/restDoclet/generation/Generator.java b/src/main/java/org/cloudifysource/restDoclet/generation/Generator.java index 1e185bd..4ae8280 100644 --- a/src/main/java/org/cloudifysource/restDoclet/generation/Generator.java +++ b/src/main/java/org/cloudifysource/restDoclet/generation/Generator.java @@ -73,9 +73,9 @@ * In default the Generator uses the velocity template * {@link RestDocConstants#VELOCITY_TEMPLATE_PATH} and writes the result to * {@link RestDocConstants#DOC_DEST_PATH}. - * + * * @author yael - * + * */ public class Generator { private static final Logger logger = Logger.getLogger(Generator.class.getName()); @@ -96,12 +96,12 @@ public class Generator { private static IDocExampleGenerator responseExampleGenerator; private static String requestBodyParamFilterName; private static IRequestBodyParamFilter requestBodyParamFilter; - + /** - * + * * @param rootDoc */ public Generator(final RootDoc rootDoc) { @@ -110,24 +110,24 @@ public Generator(final RootDoc rootDoc) { } /** - * + * * @param args . - *

This class uses the annotationType() method of class DocAnnotation, - * so if there is an annotation in the source with its class not in the class path, + *

This class uses the annotationType() method of class DocAnnotation, + * so if there is an annotation in the source with its class not in the class path, * a ClassCastException will be thrown. - *
For example, in order to use the PreAuthorize annotation, - * the spring-security-core JAR needs to be added to the class path. + *
For example, in order to use the PreAuthorize annotation, + * the spring-security-core JAR needs to be added to the class path. *
* related question in stackoverflow */ public static void main(final String[] args) { - /** - * This class uses the annotationType() method of class DocAnnotation, - * so if there is an annotation in the source which its class is not in the class path, + /** + * This class uses the annotationType() method of class DocAnnotation, + * so if there is an annotation in the source which its class is not in the class path, * a ClassCastException will be thrown. - * For example, to use the PreAuthorize annotation, - * the spring-security-core JAR need to be added to the class path. + * For example, to use the PreAuthorize annotation, + * the spring-security-core JAR need to be added to the class path. * See * related question in stackoverflow **/ @@ -135,14 +135,14 @@ public static void main(final String[] args) { RestDocConstants.DOCLET_FLAG, RestDoclet.class.getName(), RestDocConstants.SOURCE_PATH_FLAG, RestDocConstants.SOURCES_PATH, RestDocConstants.CONTROLLERS_PACKAGE, RestDocConstants.VELOCITY_TEMPLATE_PATH_FLAG, RestDocConstants.VELOCITY_TEMPLATE_PATH, - RestDocConstants.DOC_DEST_PATH_FLAG, RestDocConstants.DOC_DEST_PATH, + RestDocConstants.DOC_DEST_PATH_FLAG, RestDocConstants.DOC_DEST_PATH, RestDocConstants.DOC_CSS_PATH_FLAG, RestDocConstants.DOC_CSS_PATH, RestDocConstants.VERSION_FLAG, RestDocConstants.VERSION }); } /** - * + * * @param options */ private void setFlags(final String[][] options) { @@ -202,41 +202,41 @@ private void setFlags(final String[][] options) { } initRequestExampleGenerator(requestExampleGeneratorName); - logger.log(Level.INFO, "Updating request example generator class to " + logger.log(Level.INFO, "Updating request example generator class to " + requestExampleGenerator.getClass().getName()); initResponseExampleGenerator(responseExampleGeneratorName); - logger.log(Level.INFO, "Updating response example generator class to " + logger.log(Level.INFO, "Updating response example generator class to " + responseExampleGenerator.getClass().getName()); - + initRequestBodyParamFilter(); - logger.log(Level.INFO, "Updating request body parameter filter class to " + logger.log(Level.INFO, "Updating request body parameter filter class to " + requestBodyParamFilter.getClass().getName()); } - - + + private void initRequestBodyParamFilter() { if (StringUtils.isBlank(requestBodyParamFilterName)) { requestBodyParamFilter = new DefaultRequestBodyParameterFilter(); } else { try { Class clazz = Class.forName(requestBodyParamFilterName); - requestBodyParamFilter = (IRequestBodyParamFilter) clazz.newInstance(); + requestBodyParamFilter = (IRequestBodyParamFilter) clazz.newInstance(); } catch (Exception e) { logger.log(Level.WARNING, - "Cought " + e.getClass().getName() - + " when tried to load and instantiate class " - + requestBodyParamFilterName + "Cought " + e.getClass().getName() + + " when tried to load and instantiate class " + + requestBodyParamFilterName + ". Using a default filter class instead."); requestBodyParamFilter = new DefaultRequestBodyParameterFilter(); } } } - + private void initRequestExampleGenerator(final String exampleGeneratorName) { - IDocExampleGenerator exampleGeneratorClass = + IDocExampleGenerator exampleGeneratorClass = getExampleGeneratorClass( - IDocExampleGenerator.class, - exampleGeneratorName, + IDocExampleGenerator.class, + exampleGeneratorName, "request"); if (exampleGeneratorClass == null) { requestExampleGenerator = new DocDefaultExampleGenerator(); @@ -244,12 +244,12 @@ private void initRequestExampleGenerator(final String exampleGeneratorName) { requestExampleGenerator = exampleGeneratorClass; } } - + private void initResponseExampleGenerator(final String exampleGeneratorName) { - IDocExampleGenerator exampleGeneratorClass = + IDocExampleGenerator exampleGeneratorClass = getExampleGeneratorClass( - IDocExampleGenerator.class, - exampleGeneratorName, + IDocExampleGenerator.class, + exampleGeneratorName, "response"); if (exampleGeneratorClass == null) { responseExampleGenerator = new DocDefaultExampleGenerator(); @@ -257,34 +257,34 @@ private void initResponseExampleGenerator(final String exampleGeneratorName) { responseExampleGenerator = exampleGeneratorClass; } } - + private T getExampleGeneratorClass( - final Class expectedInterface, - final String exampleGeneratorName, + final Class expectedInterface, + final String exampleGeneratorName, final String exampleType) { if (StringUtils.isBlank(exampleGeneratorName)) { - logger.log(Level.INFO, - "No custom example generator given, using a default " + logger.log(Level.INFO, + "No custom example generator given, using a default " + exampleType + " example generator instead."); return null; - } + } Class reqExGenClass; try { reqExGenClass = Class.forName(exampleGeneratorName); } catch (ClassNotFoundException e) { logger.log(Level.WARNING, - "Cought ClassNotFoundException when tried to load the " + exampleType - + " example generator class - " - + exampleGeneratorName + "Cought ClassNotFoundException when tried to load the " + exampleType + + " example generator class - " + + exampleGeneratorName + ". Using a default generator instead."); return null; } if (!expectedInterface.isAssignableFrom(reqExGenClass)) { - logger.log(Level.WARNING, - "The given " + exampleType - + " example generator class [" + exampleGeneratorName - + "] does not implement " + expectedInterface.getName() + logger.log(Level.WARNING, + "The given " + exampleType + + " example generator class [" + exampleGeneratorName + + "] does not implement " + expectedInterface.getName() + ". Using a default generator instead."); return null; } @@ -293,16 +293,16 @@ private T getExampleGeneratorClass( return expectedInterface.cast(reqExGenClass.newInstance()); } catch (Exception e) { logger.log(Level.WARNING, - "Cought exception - " + e.getClass().getName() - + " when tried to instantiate the " + exampleType - + " example generator class [ " + exampleGeneratorName + "Cought exception - " + e.getClass().getName() + + " when tried to instantiate the " + exampleType + + " example generator class [ " + exampleGeneratorName + "]. Using a default generator instead."); return null; } } /** - * + * * @throws Exception . */ public void run() throws Exception { @@ -344,7 +344,7 @@ public void run() throws Exception { /** * Creates the REST API documentation in HTML form, using the controllers' * data and the velocity template. - * + * * @param controllers . * @return string that contains the documentation in HTML form. * @throws Exception . @@ -354,7 +354,7 @@ public String generateHtmlDocumentation(final List controllers) logger.log(Level.INFO, "Generate velocity using template: " + velocityTemplatePath - + (isUserDefineTemplatePath ? File.separator + + (isUserDefineTemplatePath ? File.separator + velocityTemplateFileName + " (got template path from user)" : "(default template path)")); @@ -386,7 +386,7 @@ public String generateHtmlDocumentation(final List controllers) } - private static List generateControllers(final ClassDoc[] classes) + private static List generateControllers(final ClassDoc[] classes) throws Exception { List controllersList = new LinkedList(); for (ClassDoc classDoc : classes) { @@ -399,11 +399,11 @@ private static List generateControllers(final ClassDoc[] classes) return controllersList; } - private static List generateControllers(final ClassDoc classDoc) + private static List generateControllers(final ClassDoc classDoc) throws Exception { - List controllers = new LinkedList(); + List controllers = new LinkedList(); List annotations = generateAnnotations(classDoc.annotations()); - + if (Utils.filterOutControllerClass(classDoc, annotations)) { return null; } @@ -411,7 +411,7 @@ private static List generateControllers(final ClassDoc classDoc) String controllerClassName = classDoc.typeName(); DocRequestMappingAnnotation requestMappingAnnotation = Utils.getRequestMappingAnnotation(annotations); if (requestMappingAnnotation == null) { - throw new IllegalArgumentException("controller class " + controllerClassName + throw new IllegalArgumentException("controller class " + controllerClassName + " is missing request mapping annotation"); } String[] uriArray = requestMappingAnnotation.getValue(); @@ -447,26 +447,23 @@ private static List generateAnnotations( } private static SortedMap generateMethods( - final MethodDoc[] methods) + final MethodDoc[] methods) throws Exception { SortedMap docMethods = new TreeMap(); for (MethodDoc methodDoc : methods) { List annotations = generateAnnotations(methodDoc.annotations()); - + // Does not handle methods without a RequestMapping annotation. if (Utils.filterOutMethod(methodDoc, annotations)) { continue; } // get all HTTP methods - DocRequestMappingAnnotation requestMappingAnnotation = Utils - .getRequestMappingAnnotation(annotations); + DocRequestMappingAnnotation requestMappingAnnotation = Utils.getRequestMappingAnnotation(annotations); String[] methodArray = requestMappingAnnotation.getMethod(); - DocHttpMethod[] docHttpMethodArray = new DocHttpMethod[methodArray.length]; - for (int i = 0; i < methodArray.length; i++) { - docHttpMethodArray[i] = generateHttpMethod(methodDoc, - methodArray[i], annotations); - } + + DocHttpMethod[] docHttpMethodArray = httpMethodDoc( + methodArray == null? new String[0] : methodArray, methodDoc, annotations); // get all URIs String[] uriArray = requestMappingAnnotation.getValue(); if (uriArray == null || uriArray.length == 0) { @@ -475,7 +472,7 @@ private static SortedMap generateMethods( } for (String uri : uriArray) { DocMethod docMethod = docMethods.get(uri); - // If method with that uri already exist, + // If method with that uri already exist, // add the current httpMethod to the existing method. // There can be several httpMethods (GET, POST, DELETE) for each // uri. @@ -491,8 +488,24 @@ private static SortedMap generateMethods( return docMethods; } - private static DocHttpMethod generateHttpMethod(final MethodDoc methodDoc, - final String httpMethodName, final List annotations) + private static DocHttpMethod[] httpMethodDoc( + final String[] methodArray, + final MethodDoc methodDoc, + final List annotations) throws Exception + { + if (methodArray.length == 0) { + return new DocHttpMethod[] {generateHttpMethod(methodDoc, "ALL", annotations)}; + } + DocHttpMethod[] docHttpMethodArray = new DocHttpMethod[methodArray.length]; + for (int i = 0; i < methodArray.length; i++) { + docHttpMethodArray[i] = generateHttpMethod(methodDoc, + methodArray[i], annotations); + } + return docHttpMethodArray; + } + + private static DocHttpMethod generateHttpMethod(final MethodDoc methodDoc, + final String httpMethodName, final List annotations) throws Exception { DocHttpMethod httpMethod = new DocHttpMethod(methodDoc.name(), httpMethodName); @@ -504,7 +517,7 @@ private static DocHttpMethod generateHttpMethod(final MethodDoc methodDoc, .getPossibleResponseStatusesAnnotation(annotations)); if (StringUtils.isBlank(httpMethod.getHttpMethodName())) { - throw new IllegalArgumentException("method " + methodDoc.name() + throw new IllegalArgumentException("method " + methodDoc.name() + " is missing request mapping annotation's method (http method)."); } @@ -512,11 +525,11 @@ private static DocHttpMethod generateHttpMethod(final MethodDoc methodDoc, } private static void generateExamples(final DocHttpMethod httpMethod, - final List annotations) + final List annotations) throws Exception { DocJsonResponseExample jsonResponseExampleAnnotation = Utils.getJsonResponseExampleAnnotation(annotations); DocJsonRequestExample jsonRequestExampleAnnotation = Utils.getJsonRequestExampleAnnotation(annotations); - + String requestExample; if (jsonRequestExampleAnnotation != null) { httpMethod.setJsonRequesteExample(jsonRequestExampleAnnotation); @@ -530,23 +543,23 @@ private static void generateExamples(final DocHttpMethod httpMethod, if (jsonResponseExampleAnnotation != null) { httpMethod.setJsonResponseExample(jsonResponseExampleAnnotation); responseExample = jsonResponseExampleAnnotation.generateJsonResponseBody(); - } else { + } else { responseExample = generateResponseExample(httpMethod); } httpMethod.setResponseExample(responseExample); - + } private static String generateRequestExmple(final DocHttpMethod httpMethod) { - + List params = httpMethod.getParams(); Type type = null; for (DocParameter docParameter : params) { if (requestBodyParamFilter.filter(httpMethod, docParameter)) { - type = docParameter.getType(); + type = docParameter.getType(); break; } - } + } if (type == null) { return REQUEST_HAS_NO_BODY_MSG; } @@ -555,13 +568,13 @@ private static String generateRequestExmple(final DocHttpMethod httpMethod) { generateExample = requestExampleGenerator.generateExample(type); generateExample = Utils.getIndentJson(generateExample); } catch (Exception e) { - logger.warning("Could not generate request example for method: " + httpMethod.getMethodSignatureName() - + " with the request parameter type " + type.qualifiedTypeName() + logger.warning("Could not generate request example for method: " + httpMethod.getMethodSignatureName() + + " with the request parameter type " + type.qualifiedTypeName() + ". Exception was: " + e); generateExample = RestDocConstants.FAILED_TO_CREATE_REQUEST_EXAMPLE + "." - + LINE_SEPARATOR + + LINE_SEPARATOR + "Parameter type: " + type.qualifiedTypeName() + "." - + LINE_SEPARATOR + + LINE_SEPARATOR + "The exception caught was " + e; } return generateExample; @@ -578,15 +591,15 @@ private static String generateResponseExample(final DocHttpMethod httpMethod) { generateExample = responseExampleGenerator.generateExample(returnType); generateExample = Utils.getIndentJson(generateExample); } catch (Exception e) { - logger.warning("Could not generate response example for method: " + httpMethod.getMethodSignatureName() + logger.warning("Could not generate response example for method: " + httpMethod.getMethodSignatureName() + " with the return value type [" + typeName + "]. Exception was: " + e); generateExample = RestDocConstants.FAILED_TO_CREATE_RESPONSE_EXAMPLE - + LINE_SEPARATOR + + LINE_SEPARATOR + "Return value type: " + typeName + "." - + LINE_SEPARATOR + + LINE_SEPARATOR + "The exception caught was " + e; } - + return generateExample; } From 4213898ba4dad43b6bc55aac648ae606b9c8bd2d Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 4 Oct 2013 11:00:03 +0100 Subject: [PATCH 2/6] example object creation that recursively instantiates fields --- .gitignore | 1 + pom.xml | 17 +++- .../DocDefaultExampleGenerator.java | 17 ++-- .../exampleGenerators/ObjectCreator.java | 80 +++++++++++++++++++ .../exampleGenerators/ObjectCreatorTest.java | 76 ++++++++++++++++++ 5 files changed, 177 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java create mode 100644 src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java diff --git a/.gitignore b/.gitignore index 6256558..c54fb7e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.jar *.war *.ear +.idea .classpath .project .settings/** diff --git a/pom.xml b/pom.xml index 636e8c3..f512781 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cloudifysource rest-doclet - 0.5.2 + 0.5.3 3.1.3.RELEASE @@ -20,9 +20,15 @@ junit junit - 4.8.2 + 4.11 test + + org.hamcrest + hamcrest-all + test + 1.3 + org.springframework spring-webmvc @@ -49,6 +55,11 @@ jackson-core-asl 1.9.9 + + org.objenesis + objenesis + 1.2 + @@ -108,4 +119,4 @@ rest-doclet - \ No newline at end of file + diff --git a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java index 969aca2..a5cff50 100644 --- a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java +++ b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java @@ -22,14 +22,16 @@ import com.sun.javadoc.Type; /** - * Creates a default example - - * creates a JSON format of an instantiated object of the given clazz. + * Creates a default example - + * creates a JSON format of an instantiated object of the given clazz. * @author yael * @since 0.5.0 */ public class DocDefaultExampleGenerator implements IDocExampleGenerator { + private ObjectCreator objectCreator_ = new ObjectCreator(); + private String generateJSON(final Type type) throws Exception { Class clazz = ClassUtils.getClass(type.qualifiedTypeName()); if (MultipartFile.class.getName().equals(clazz.getName())) { @@ -39,18 +41,11 @@ private String generateJSON(final Type type) throws Exception { throw new InstantiationException( "the given class is an interface [" + clazz.getName() + "]."); } - Object newInstance = null; - if (clazz.isPrimitive()) { - newInstance = PrimitiveExampleValues.getValue(clazz); - } - Class classToInstantiate = clazz; - if (newInstance == null) { - newInstance = classToInstantiate.newInstance(); - } + Object newInstance = objectCreator_.createObject(clazz); return new ObjectMapper().writeValueAsString(newInstance); } - @Override + @Override public String generateExample(final Type type) throws Exception { return generateJSON(type); } diff --git a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java new file mode 100644 index 0000000..1377180 --- /dev/null +++ b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java @@ -0,0 +1,80 @@ +package org.cloudifysource.restDoclet.exampleGenerators; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.util.LinkedList; +import java.util.List; + +import org.objenesis.Objenesis; +import org.objenesis.ObjenesisStd; + +/** + * @author Ed Kimber + */ +public class ObjectCreator { + + private Objenesis objenesis_; + + public ObjectCreator() { + objenesis_ = new ObjenesisStd(); + } + + public Object createObject(final Class cls) throws IllegalAccessException { + Object object = objenesis_.getInstantiatorOf(cls).newInstance(); + instantiateFieldsOn(object); + return object; + } + + public void instantiateFieldsOn(final Object object) throws IllegalAccessException { + Class cls = object.getClass(); + for (Field f : cls.getDeclaredFields()) { + f.setAccessible(true); + if (f.getGenericType() instanceof ParameterizedType) { + f.set(object, createParameterizedType(f.getType(), (ParameterizedType) f.getGenericType())); + } + else { + f.set(object, createValueForType(f.getType())); + } + } + } + + /** + * Only Lists with one simple generic param currently supported. + */ + private Object createParameterizedType(final Class base, final ParameterizedType genericType) + throws IllegalAccessException + { + Class type = (Class) genericType.getActualTypeArguments()[0]; + if (List.class.isAssignableFrom(base)) { + return createListOfType(type); + } + else { + return createObject(base); + } + } + + private Object createValueForType(final Class cls) throws IllegalAccessException { + if (String.class.isAssignableFrom(cls)) { + return "foo"; + } + else if (List.class.isAssignableFrom(cls)) { + return createListOfType(Object.class); + } + else if (cls.isPrimitive()) { + return null; + } + else { + return createObject(cls); + } + } + + @SuppressWarnings("unchecked") + private List createListOfType(final Class type) throws IllegalAccessException { + LinkedList list = new LinkedList(); + list.add(createObject(type)); + list.add(createObject(type)); + return list; + } + + +} diff --git a/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java b/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java new file mode 100644 index 0000000..9576315 --- /dev/null +++ b/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java @@ -0,0 +1,76 @@ +package org.cloudifysource.restDoclet.exampleGenerators; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.notNullValue; + +/** + * @author edward + */ +public class ObjectCreatorTest { + + private ObjectCreator objectCreator_; + + @Before + public void setup() { + objectCreator_ = new ObjectCreator(); + } + + @Test + public void createsAnObject() throws IllegalAccessException { + assertThat(objectCreator_.createObject(Fish.class), instanceOf(Fish.class)); + } + + @Test + public void setsAStringFieldOnAnObject() throws IllegalAccessException { + Fish fish = (Fish) objectCreator_.createObject(Fish.class); + assertThat(fish.getName(), notNullValue()); + assertThat(fish.getName().length(), greaterThan(0)); + } + + @Test + public void createsNestedClasses() throws IllegalAccessException { + FishBowl fishBowl = (FishBowl) objectCreator_.createObject(FishBowl.class); + assertThat(fishBowl.getFish(), instanceOf(Fish.class)); + assertThat(fishBowl.getFish().getName(), notNullValue()); + } + + @Test + public void createsLists() throws IllegalAccessException { + Aquarium aquarium = (Aquarium) objectCreator_.createObject(Aquarium.class); + assertThat(aquarium.getFishes(), instanceOf(List.class)); + assertThat(aquarium.getFishes(), hasSize(greaterThan(0))); + assertThat(aquarium.getFishes().get(0), instanceOf(Fish.class)); + assertThat(aquarium.getFishes().get(0).getName(), notNullValue()); + } + + static class Fish { + private String name_; + + public String getName() { + return name_; + } + } + + static class FishBowl { + private Fish fish_; + + public Fish getFish() { + return fish_; + } + } + + static class Aquarium { + private List fishes_; + + public List getFishes() { + return fishes_; + } + } +} From 715a0169ea1a76f75ca03ed103ab299000aaceff Mon Sep 17 00:00:00 2001 From: edward Date: Fri, 4 Oct 2013 16:36:02 +0100 Subject: [PATCH 3/6] object creator can create abstract objects or interfaces and return objects from their abstract methods --- pom.xml | 11 ++- .../DocDefaultExampleGenerator.java | 14 +++- .../exampleGenerators/ObjectCreator.java | 68 ++++++++++++++++--- .../exampleGenerators/ObjectCreatorTest.java | 48 +++++++++++++ 4 files changed, 126 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index f512781..5ce5c70 100644 --- a/pom.xml +++ b/pom.xml @@ -3,10 +3,10 @@ 4.0.0 org.cloudifysource rest-doclet - 0.5.3 + 0.5.4 - 3.1.3.RELEASE + 3.2.4.RELEASE @@ -34,6 +34,11 @@ spring-webmvc ${springVersion} + + org.springframework + spring-context + ${springVersion} + javax.servlet servlet-api @@ -60,7 +65,7 @@ objenesis 1.2 - + default-profile diff --git a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java index a5cff50..67937de 100644 --- a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java +++ b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java @@ -17,6 +17,7 @@ import org.apache.commons.lang.ClassUtils; import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; import org.springframework.web.multipart.MultipartFile; import com.sun.javadoc.Type; @@ -41,8 +42,17 @@ private String generateJSON(final Type type) throws Exception { throw new InstantiationException( "the given class is an interface [" + clazz.getName() + "]."); } - Object newInstance = objectCreator_.createObject(clazz); - return new ObjectMapper().writeValueAsString(newInstance); + + Object newInstance; + if (clazz.isPrimitive()) { + newInstance = PrimitiveExampleValues.getValue(clazz); + } + else { + newInstance = objectCreator_.createObject(clazz); + } + return new ObjectMapper() + .configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false) + .writeValueAsString(newInstance); } @Override diff --git a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java index 1377180..f7d1aa5 100644 --- a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java +++ b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java @@ -1,12 +1,18 @@ package org.cloudifysource.restDoclet.exampleGenerators; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.util.LinkedList; import java.util.List; +import java.util.logging.Logger; import org.objenesis.Objenesis; import org.objenesis.ObjenesisStd; +import org.springframework.cglib.proxy.Enhancer; +import org.springframework.cglib.proxy.MethodInterceptor; +import org.springframework.cglib.proxy.MethodProxy; /** * @author Ed Kimber @@ -14,28 +20,66 @@ public class ObjectCreator { private Objenesis objenesis_; + private static final Logger logger_ = Logger.getLogger(ObjectCreator.class.getName()); + public ObjectCreator() { objenesis_ = new ObjenesisStd(); } public Object createObject(final Class cls) throws IllegalAccessException { - Object object = objenesis_.getInstantiatorOf(cls).newInstance(); - instantiateFieldsOn(object); - return object; + if (isAbstractOrInterface(cls)) return createProxy(cls); + + try { + Object object = objenesis_.newInstance(cls); + instantiateFieldsOn(object); + return object; + } + catch (IllegalAccessError illegal) { + logger_.warning("Could not instantiate an object of class: " + cls.getName()); + return null; + } } public void instantiateFieldsOn(final Object object) throws IllegalAccessException { Class cls = object.getClass(); for (Field f : cls.getDeclaredFields()) { - f.setAccessible(true); - if (f.getGenericType() instanceof ParameterizedType) { - f.set(object, createParameterizedType(f.getType(), (ParameterizedType) f.getGenericType())); + if (f.getType().equals(cls)) { + continue; //avoid infinite recursion! + } + else { + tryToSetField(f, object); + } + } + } + + private void tryToSetField(final Field field, final Object object) { + try { + field.setAccessible(true); + Class fieldType = field.getType(); + + if (field.getGenericType() instanceof ParameterizedType) { + field.set(object, createParameterizedType(fieldType, (ParameterizedType) field.getGenericType())); } else { - f.set(object, createValueForType(f.getType())); + field.set(object, createValueForType(fieldType)); } } + catch (IllegalAccessException illegal) { + logger_.warning("Could not set field " + field.getName() + " on a " + object.getClass()); + } + } + + public Object createProxy(final Class cls) { + return Enhancer.create(cls, new MethodInterceptor() { + @Override + public Object intercept(final Object proxy, final Method method, + final Object[] args, final MethodProxy methodProxy) + throws Throwable + { + return createValueForType(method.getReturnType()); + } + }); } /** @@ -61,7 +105,7 @@ else if (List.class.isAssignableFrom(cls)) { return createListOfType(Object.class); } else if (cls.isPrimitive()) { - return null; + return PrimitiveExampleValues.getValue(cls); } else { return createObject(cls); @@ -71,10 +115,14 @@ else if (cls.isPrimitive()) { @SuppressWarnings("unchecked") private List createListOfType(final Class type) throws IllegalAccessException { LinkedList list = new LinkedList(); - list.add(createObject(type)); - list.add(createObject(type)); + list.add(createValueForType(type)); + list.add(createValueForType(type)); return list; } + private boolean isAbstractOrInterface(final Class cls) { + int modifiers = cls.getModifiers(); + return Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers); + } } diff --git a/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java b/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java index 9576315..6c119b4 100644 --- a/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java +++ b/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java @@ -34,6 +34,12 @@ public void setsAStringFieldOnAnObject() throws IllegalAccessException { assertThat(fish.getName().length(), greaterThan(0)); } + @Test + public void setsAPrimitiveFieldOnAnObject() throws IllegalAccessException { + Fish fish = (Fish) objectCreator_.createObject(Fish.class); + assertThat(fish.getCount(), instanceOf(long.class)); + } + @Test public void createsNestedClasses() throws IllegalAccessException { FishBowl fishBowl = (FishBowl) objectCreator_.createObject(FishBowl.class); @@ -50,12 +56,54 @@ public void createsLists() throws IllegalAccessException { assertThat(aquarium.getFishes().get(0).getName(), notNullValue()); } + @Test + public void createsEmptyClasses() throws IllegalAccessException { + EmptyClass empty = (EmptyClass) objectCreator_.createObject(EmptyClass.class); + } + + @Test + public void canCreateAnAbstractClass() throws IllegalAccessException { + ClassWithAnAbstractClassInside classInside = + (ClassWithAnAbstractClassInside) objectCreator_.createObject(ClassWithAnAbstractClassInside.class); + + assertThat(classInside.getTheAbstractClass(), notNullValue()); + } + + @Test + public void canCallAbstractMethods() throws IllegalAccessException { + AbstractClass abstractClass = (AbstractClass) objectCreator_.createObject(AbstractClass.class); + assertThat(abstractClass.getFoo(), notNullValue()); + } + + static class EmptyClass { + public static final EmptyClass NOTHING = new EmptyClass(); + } + + static class ClassWithAnAbstractClassInside { + private AbstractClass abstractClass_; + + public AbstractClass getTheAbstractClass() { + return abstractClass_; + } + } + + static abstract class AbstractClass { + private String bar_; + + public abstract String getFoo(); + } + static class Fish { private String name_; + private long count_; public String getName() { return name_; } + + public long getCount() { + return count_; + } } static class FishBowl { From 4330889810d5de7999d89ce57d2e68da3a873f06 Mon Sep 17 00:00:00 2001 From: edward Date: Tue, 22 Oct 2013 15:16:06 +0100 Subject: [PATCH 4/6] date and enum creators --- pom.xml | 7 +- .../exampleGenerators/ObjectCreator.java | 91 ++++++++++++++++--- .../exampleGenerators/ObjectCreatorTest.java | 37 ++++++++ 3 files changed, 122 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 5ce5c70..6c56a61 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cloudifysource rest-doclet - 0.5.4 + 0.5.5-SNAPSHOT 3.2.4.RELEASE @@ -65,6 +65,11 @@ objenesis 1.2 + + com.google.guava + guava + 15.0 + diff --git a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java index f7d1aa5..e9f5338 100644 --- a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java +++ b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java @@ -4,16 +4,23 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.logging.Logger; +import org.apache.commons.lang.StringUtils; import org.objenesis.Objenesis; import org.objenesis.ObjenesisStd; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; +import static com.google.common.collect.Lists.newArrayList; + /** * @author Ed Kimber */ @@ -21,10 +28,11 @@ public class ObjectCreator { private Objenesis objenesis_; private static final Logger logger_ = Logger.getLogger(ObjectCreator.class.getName()); - + private List exampleCreators_; public ObjectCreator() { objenesis_ = new ObjenesisStd(); + exampleCreators_ = newArrayList(primitiveCreator_, stringCreator_, enumCreator_, dateCreator_, listCreator_); } public Object createObject(final Class cls) throws IllegalAccessException { @@ -98,18 +106,12 @@ private Object createParameterizedType(final Class base, final ParameterizedType } private Object createValueForType(final Class cls) throws IllegalAccessException { - if (String.class.isAssignableFrom(cls)) { - return "foo"; - } - else if (List.class.isAssignableFrom(cls)) { - return createListOfType(Object.class); - } - else if (cls.isPrimitive()) { - return PrimitiveExampleValues.getValue(cls); - } - else { - return createObject(cls); + for (ExampleCreator creator : exampleCreators_) { + if (creator.match(cls)) { + return creator.create(cls); + } } + return createObject(cls); } @SuppressWarnings("unchecked") @@ -125,4 +127,69 @@ private boolean isAbstractOrInterface(final Class cls) { return Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers); } + interface ExampleCreator { + boolean match(Class cls); + Object create(Class cls) throws IllegalAccessException; + } + + private static ExampleCreator stringCreator_ = new ExampleCreator() { + + @Override + public boolean match(Class cls) { + return String.class.isAssignableFrom(cls); + } + + @Override + public Object create(final Class cls) { + return "foo"; + } + }; + + private static ExampleCreator primitiveCreator_ = new ExampleCreator() { + @Override + public boolean match(final Class cls) { + return cls.isPrimitive(); + } + + @Override + public Object create(final Class cls) { + return PrimitiveExampleValues.getValue(cls); + } + }; + + private static ExampleCreator enumCreator_ = new ExampleCreator() { + @Override + public boolean match(final Class cls) { + return cls.isEnum(); + } + + @Override + public Object create(final Class cls) { + return Enum.valueOf((Class) cls, cls.getEnumConstants()[0].toString()); + } + }; + + private ExampleCreator listCreator_ = new ExampleCreator() { + @Override + public boolean match(final Class cls) { + return List.class.isAssignableFrom(cls); + } + + @Override + public Object create(final Class cls) throws IllegalAccessException { + return createListOfType(Object.class); + } + }; + + private ExampleCreator dateCreator_ = new ExampleCreator() { + @Override + public boolean match(final Class cls) { + return Date.class.isAssignableFrom(cls); + } + + @Override + public Object create(final Class cls) throws IllegalAccessException { + return new Date(); + } + }; } diff --git a/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java b/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java index 6c119b4..71197d5 100644 --- a/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java +++ b/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java @@ -1,5 +1,6 @@ package org.cloudifysource.restDoclet.exampleGenerators; +import java.util.Date; import java.util.List; import org.junit.Before; @@ -8,7 +9,10 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; /** * @author edward @@ -56,6 +60,12 @@ public void createsLists() throws IllegalAccessException { assertThat(aquarium.getFishes().get(0).getName(), notNullValue()); } + @Test + public void createClassWithEnumField() throws IllegalAccessException { + ClassWithEnumField response = (ClassWithEnumField) objectCreator_.createObject(ClassWithEnumField.class); + assertThat(response.getStatus(), is(ClassWithEnumField.Status.ACTIVATED)); + } + @Test public void createsEmptyClasses() throws IllegalAccessException { EmptyClass empty = (EmptyClass) objectCreator_.createObject(EmptyClass.class); @@ -75,6 +85,12 @@ public void canCallAbstractMethods() throws IllegalAccessException { assertThat(abstractClass.getFoo(), notNullValue()); } + @Test + public void canCreateDateField() throws IllegalAccessException { + DateClass dateClass = (DateClass) objectCreator_.createObject(DateClass.class); + assertThat(dateClass.getDate(), not(nullValue())); + } + static class EmptyClass { public static final EmptyClass NOTHING = new EmptyClass(); } @@ -121,4 +137,25 @@ public List getFishes() { return fishes_; } } + + public static class ClassWithEnumField { + public enum Status { + ACTIVATED, + PENDING + } + + private Status status_ = Status.ACTIVATED; + + public Status getStatus() { + return status_; + } + } + + public static class DateClass { + private Date date_; + + public Date getDate() { + return date_; + } + } } From f607a2d6ef7211671abe5b758543654b752ac3c0 Mon Sep 17 00:00:00 2001 From: edward Date: Tue, 22 Oct 2013 16:31:48 +0100 Subject: [PATCH 5/6] can handle primitive wrappers as well as primitives --- pom.xml | 2 +- .../exampleGenerators/ObjectCreator.java | 15 ++++++++++++++- .../exampleGenerators/ObjectCreatorTest.java | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6c56a61..c8fdbbb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cloudifysource rest-doclet - 0.5.5-SNAPSHOT + 0.5.6-SNAPSHOT 3.2.4.RELEASE diff --git a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java index e9f5338..e34ea1e 100644 --- a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java +++ b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreator.java @@ -19,6 +19,7 @@ import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; +import com.google.common.primitives.Primitives; import static com.google.common.collect.Lists.newArrayList; /** @@ -32,7 +33,7 @@ public class ObjectCreator { public ObjectCreator() { objenesis_ = new ObjenesisStd(); - exampleCreators_ = newArrayList(primitiveCreator_, stringCreator_, enumCreator_, dateCreator_, listCreator_); + exampleCreators_ = newArrayList(primitiveCreator_, wrapperCreator_, stringCreator_, enumCreator_, dateCreator_, listCreator_); } public Object createObject(final Class cls) throws IllegalAccessException { @@ -157,6 +158,18 @@ public Object create(final Class cls) { } }; + private static ExampleCreator wrapperCreator_ = new ExampleCreator() { + @Override + public boolean match(final Class cls) { + return Primitives.isWrapperType(cls); + } + + @Override + public Object create(final Class cls) throws IllegalAccessException { + return PrimitiveExampleValues.getValue(Primitives.unwrap(cls)); + } + }; + private static ExampleCreator enumCreator_ = new ExampleCreator() { @Override public boolean match(final Class cls) { diff --git a/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java b/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java index 71197d5..5f9796e 100644 --- a/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java +++ b/src/test/java/org/cloudifysource/restDoclet/exampleGenerators/ObjectCreatorTest.java @@ -111,7 +111,7 @@ static abstract class AbstractClass { static class Fish { private String name_; - private long count_; + private Long count_; public String getName() { return name_; From 5ad4212742f7e272435991531645a74af7bf573a Mon Sep 17 00:00:00 2001 From: edward Date: Wed, 23 Oct 2013 15:19:18 +0100 Subject: [PATCH 6/6] also try to generate interfaces/abstracts from the top level of rest response class hierarchy --- pom.xml | 9 ++++++++- .../DocDefaultExampleGenerator.java | 12 +----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index c8fdbbb..cfd7a35 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cloudifysource rest-doclet - 0.5.6-SNAPSHOT + 0.5.8 3.2.4.RELEASE @@ -129,4 +129,11 @@ rest-doclet + + + aristaeus.mediagraft.com + aristaeus.mediagraft.com-releases + http://aristaeus.mediagraft.com:8080/libs-release-local + + diff --git a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java index 67937de..2676cac 100644 --- a/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java +++ b/src/main/java/org/cloudifysource/restDoclet/exampleGenerators/DocDefaultExampleGenerator.java @@ -38,18 +38,8 @@ private String generateJSON(final Type type) throws Exception { if (MultipartFile.class.getName().equals(clazz.getName())) { return "\"file content\""; } - if (clazz.isInterface()) { - throw new InstantiationException( - "the given class is an interface [" + clazz.getName() + "]."); - } - Object newInstance; - if (clazz.isPrimitive()) { - newInstance = PrimitiveExampleValues.getValue(clazz); - } - else { - newInstance = objectCreator_.createObject(clazz); - } + Object newInstance = objectCreator_.createObject(clazz); return new ObjectMapper() .configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false) .writeValueAsString(newInstance);