You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2015/12/10 18:08:54 UTC

isis git commit: ISIS-1237: now generates models for object, object collection and actions

Repository: isis
Updated Branches:
  refs/heads/ISIS-1237 3d9f8fe03 -> 4502dc10a


ISIS-1237: now generates models for object, object collection and actions


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/4502dc10
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/4502dc10
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/4502dc10

Branch: refs/heads/ISIS-1237
Commit: 4502dc10a89c039d8350cb02b3437de1f0477ad6
Parents: 3d9f8fe
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Thu Dec 10 17:08:43 2015 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Thu Dec 10 17:08:43 2015 +0000

----------------------------------------------------------------------
 .../_migration-notes_1.10.0-to-1.11.0.adoc      |  27 +++
 .../swagger/internal/SwaggerSpecGenerator.java  | 188 ++++++++++++++++---
 .../swagger/internal/ValuePropertyFactory.java  | 174 +++++++++++++++++
 .../java/domainapp/dom/simple/SimpleObject.java |   9 +
 .../webapp/src/main/webapp/about/index.html     |  19 +-
 5 files changed, 382 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/4502dc10/adocs/documentation/src/main/asciidoc/_migration-notes_1.10.0-to-1.11.0.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/_migration-notes_1.10.0-to-1.11.0.adoc b/adocs/documentation/src/main/asciidoc/_migration-notes_1.10.0-to-1.11.0.adoc
index 65128a4..6dbd3b3 100644
--- a/adocs/documentation/src/main/asciidoc/_migration-notes_1.10.0-to-1.11.0.adoc
+++ b/adocs/documentation/src/main/asciidoc/_migration-notes_1.10.0-to-1.11.0.adoc
@@ -29,3 +29,30 @@ If your application is bootstrapped using an `AppManifest` (recommended; ee
 xref:rgcms.adoc#_rgcms_classes_AppManifest-bootstrapping[here]) then the `JaxbService` will automatically be discovered and
  registered.  However, if you are still using the older `isis.properties` configuration file to explicitly register
  services then you will need to add in this service.
+
+
+== Swagger
+
+Update the `web.xml`:
+
+[source,xml]
+----
+<servlet>
+    <servlet-name>WebjarsServlet</servlet-name>
+    <servlet-class>org.webjars.servlet.WebjarsServlet</servlet-class>
+    <init-param>
+        <param-name>disableCache</param-name>
+        <param-value>false</param-value>
+    </init-param>
+    <load-on-startup>2</load-on-startup>
+</servlet>
+<servlet-mapping>
+    <servlet-name>WebjarsServlet</servlet-name>
+    <url-pattern>/webjars/*</url-pattern>
+</servlet-mapping>
+----
+
+
+The text of the simple archetype's `about/index.html` has also changed; the `<li`> for the `/restful` URL has been
+replaced with a `/swagger-ui` URL.
+

http://git-wip-us.apache.org/repos/asf/isis/blob/4502dc10/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java
index f1ca3e5..c58ee23 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java
@@ -29,6 +29,7 @@ import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.services.swagger.SwaggerService;
 import org.apache.isis.core.commons.factory.InstanceUtil;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
 import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
 import org.apache.isis.core.metamodel.facets.object.mixin.MixinFacet;
 import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
@@ -61,6 +62,7 @@ import io.swagger.util.Yaml;
 public class SwaggerSpecGenerator {
 
     private final SpecificationLoader specificationLoader;
+    private final ValuePropertyFactory valuePropertyFactory = new ValuePropertyFactory();
 
     public SwaggerSpecGenerator(final SpecificationLoader specificationLoader) {
         this.specificationLoader = specificationLoader;
@@ -122,7 +124,7 @@ public class SwaggerSpecGenerator {
             appendServicePath(swagger, serviceSpec);
 
             for (final ObjectAction serviceAction : serviceActions) {
-                appendServiceActionInvokePath(swagger, serviceAction);
+                appendServiceActionInvokePath(swagger, serviceSpec, serviceAction);
             }
         }
     }
@@ -157,14 +159,14 @@ public class SwaggerSpecGenerator {
                 continue;
             }
             final ModelImpl isisModel = appendObjectPathAndModelDefinitions(swagger, objectSpec);
-            appendObjectModel(isisModel, objectProperties, objectCollections);
+            updateObjectModel(isisModel, objectSpec, objectProperties, objectCollections);
 
             for (final OneToManyAssociation objectCollection : objectCollections) {
-                appendCollectionTo(swagger, objectCollection);
+                appendCollectionTo(swagger, objectSpec, objectCollection);
             }
 
             for (final ObjectAction objectAction : objectActions) {
-                appendObjectActionInvokePath(swagger, objectAction);
+                appendObjectActionInvokePath(swagger, objectSpec, objectAction);
             }
         }
     }
@@ -258,17 +260,26 @@ public class SwaggerSpecGenerator {
         swagger.addDefinition("LinkModel",
                 new ModelImpl()
                     .type("object")
-                    .property("rel", stringProperty())
-                    .property("href", stringProperty())
-                    .property("title", stringProperty())
-                    .property("method", stringPropertyEnum("GET", "POST", "PUT", "DELETE"))
-                    .property("type", stringProperty())
-                    .property("arguments", new ObjectProperty())
+                    .property("rel", stringProperty().description("the relationship of the resource to this referencing resource"))
+                    .property("href", stringProperty().description("the hyperlink reference (URL) of the resource"))
+                    .property("title", stringProperty().description("title to render"))
+                    .property("method", stringPropertyEnum("GET", "POST", "PUT", "DELETE").description("HTTP verb to access"))
+                    .property("type", stringProperty().description("Content-Type recognized by the resource (for HTTP Accept header)"))
+                    .property("arguments", new ObjectProperty().description("Any arguments, to send as query strings or in body"))
+                    .property("value", stringProperty().description("the representation of the link if followed"))
                     .required("rel")
                     .required("href")
                     .required("method")
         );
 
+        swagger.addDefinition("HrefModel",
+                new ModelImpl()
+                    .type("object")
+                    .description("Abbreviated version of the Link resource, used primarily to reference non-value objects")
+                    .property("href", stringProperty().description("the hyperlink reference (URL) of the resource"))
+                    .required("href")
+        );
+
     }
 
     void appendServicePath(final Swagger swagger, final ObjectSpecification objectSpec) {
@@ -307,8 +318,6 @@ public class SwaggerSpecGenerator {
         final Path path = new Path();
         swagger.path(String.format("/objects/%s/{objectId}", objectType), path);
 
-        final String isisModelDefinition = objectType + "Model";
-        final String restfulObjectsModelDefinition = objectType + "RestfulObjectsModel";
         final String tag = objectType.startsWith("org.apache.isis")? "> apache isis internals": objectType;
         final Operation operation = new Operation();
         path.get(
@@ -328,6 +337,7 @@ public class SwaggerSpecGenerator {
         // per https://github.com/swagger-api/swagger-spec/issues/146, swagger 2.0 doesn't support multiple
         // modelled representations per path and response code;
         // in particular cannot associate representation/model with Accept header ('produces(...) method)
+        final String restfulObjectsModelDefinition = objectType + "RestfulObjectsModel";
         if (false) {
             operation.response(200,
                     newResponse(Caching.TRANSACTIONAL)
@@ -343,13 +353,12 @@ public class SwaggerSpecGenerator {
             swagger.addDefinition(restfulObjectsModelDefinition, roSpecModel);
         }
 
+        final String isisModelDefinition = objectType + "Model";
         operation
                 .response(200,
                         newResponse(Caching.TRANSACTIONAL)
-                                .description("if Accept: application/json;profile=urn:org.apache.isis/v1")
-                                .schema(new RefProperty("#/definitions/"
-                                        + isisModelDefinition)));
-
+                                .description(objectType + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
+                                .schema(new RefProperty("#/definitions/" + isisModelDefinition)));
 
         final ModelImpl isisModel = new ModelImpl();
         swagger.addDefinition(isisModelDefinition, isisModel);
@@ -380,9 +389,9 @@ public class SwaggerSpecGenerator {
 
     void appendServiceActionInvokePath(
             final Swagger swagger,
+            final ObjectSpecification serviceSpec,
             final ObjectAction serviceAction) {
 
-        final ObjectSpecification serviceSpec = serviceAction.getOnType();
         final String serviceId = serviceIdFor(serviceSpec);
         final String actionId = serviceAction.getId();
 
@@ -457,16 +466,55 @@ public class SwaggerSpecGenerator {
         invokeOperation
                 .response(
                         200, new Response()
-                                .description(roSpecForResponseOf(serviceAction) + ": (invoke) representation of " + serviceId + "#" + actionId)
-                                .schema(new ObjectProperty())
+                                .description(serviceId + "#" + actionId + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
+                                .schema(actionReturnTypeFor(serviceAction))
                 );
     }
 
+    void appendCollectionTo(
+            final Swagger swagger,
+            final ObjectSpecification objectSpec,
+            final OneToManyAssociation collection) {
+
+        final String objectType = objectTypeFor(objectSpec);
+        final String collectionId = collection.getId();
+
+        final Path path = new Path();
+        swagger.path(String.format("/objects/%s/{objectId}/collections/%s", objectType, collectionId), path);
+
+        final Operation collectionOperation =
+                new Operation()
+                        .tag(objectType)
+                        .description(Util.roSpec("17.1") + ": resource of " + objectType + "#" + collectionId)
+                        .parameter(
+                                new PathParameter()
+                                        .name("objectId")
+                                        .type("string"))
+                        .produces("application/json;profile=urn:org.apache.isis/v1")
+                        .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/object-collection");
+
+        path.get(collectionOperation);
+        collectionOperation
+                .response(
+                        200, new Response()
+                                .description(objectType + "#" + collectionId + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
+                                .schema(modelFor(collection))
+                );
+    }
+
+    private Property modelFor(final OneToManyAssociation collection) {
+        ObjectSpecification collectionSpecification = collection.getSpecification();
+        return new ArrayProperty()
+                .description("List of " + objectTypeFor(collectionSpecification))
+                    .items(modelFor(collectionSpecification));
+    }
+
     void appendObjectActionInvokePath(
             final Swagger swagger,
+            final ObjectSpecification objectSpec,
             final ObjectAction objectAction) {
 
-        final ObjectSpecification objectSpec = objectAction.getOnType();
         final String objectType = objectTypeFor(objectSpec);
         final String actionId = objectAction.getId();
 
@@ -544,19 +592,97 @@ public class SwaggerSpecGenerator {
         invokeOperation
                 .response(
                         200, new Response()
-                                .description(roSpecForResponseOf(objectAction) + ": (invoke) representation of " + objectType + "#" + actionId)
-                                .schema(new ObjectProperty())
+                                .description(objectType + "#" + actionId)
+                                .schema(actionReturnTypeFor(objectAction))
                 );
     }
 
-    void appendCollectionTo(final Swagger members, final OneToManyAssociation serviceAction) {
+    Property actionReturnTypeFor(final ObjectAction objectAction) {
+
+        final ObjectSpecification specification = objectAction.getReturnType();
+        TypeOfFacet typeOfFacet = objectAction.getFacet(TypeOfFacet.class);
+        if(typeOfFacet != null) {
+            ObjectSpecification elementSpec = typeOfFacet.valueSpec();
+            if(elementSpec != null) {
+                return new ArrayProperty()
+                        .items(modelFor(elementSpec));
+            }
+        }
+        return modelFor(specification);
+    }
+
+    private Property modelFor(final ObjectSpecification specification) {
+        if(specification == null) {
+            return new ObjectProperty();
+        }
+
+        // no "simple" representation for void or values
+        final Class<?> correspondingClass = specification.getCorrespondingClass();
+        if(correspondingClass == void.class || correspondingClass == Void.class) {
+            return new ObjectProperty();
+        }
+        // no "simple" representation for values
+        final Property property = valuePropertyFactory.newProperty(correspondingClass);
+        if(property != null) {
+            // was recognized as a value
+            return new ObjectProperty();
+        }
+
+        if(specification.isParentedOrFreeCollection()) {
+            TypeOfFacet typeOfFacet = specification.getFacet(TypeOfFacet.class);
+            if(typeOfFacet != null) {
+                ObjectSpecification elementSpec = typeOfFacet.valueSpec();
+                if(elementSpec != null) {
+                    return new ArrayProperty()
+                            .items(modelFor(elementSpec));
+                }
+            }
+        }
 
+        return new RefProperty("#/definitions/" + objectTypeFor(specification) + "Model");
     }
 
-    void appendObjectModel(
-            final ModelImpl swagger,
-            final List<OneToOneAssociation> objectProperties, final List<OneToManyAssociation> objectCollections) {
+    void updateObjectModel(
+            final ModelImpl model,
+            final ObjectSpecification objectSpecification,
+            final List<OneToOneAssociation> objectProperties,
+            final List<OneToManyAssociation> objectCollections) {
 
+        final String objectType = objectTypeFor(objectSpecification);
+        final String className = objectSpecification.getFullIdentifier();
+
+        model
+                .type("object")
+                .description(String.format("%s (%s)", objectType, className));
+
+        for (OneToOneAssociation objectProperty : objectProperties) {
+            model.property(
+                    objectProperty.getId(),
+                    propertyFor(objectProperty.getSpecification()));
+        }
+
+        for (OneToManyAssociation objectCollection : objectCollections) {
+            final ObjectSpecification elementSpec = objectCollection.getSpecification();
+            final String elementModelDefinition = objectTypeFor(elementSpec) + "Model";
+
+            model.property(
+                    objectCollection.getId(),
+                    new ArrayProperty()
+                    .items(new RefProperty("#/definitions/" + elementModelDefinition))
+            );
+        }
+    }
+
+    Property propertyFor(final ObjectSpecification objectSpecification) {
+        final Property property =
+                valuePropertyFactory.newProperty(objectSpecification.getCorrespondingClass());
+        if (property != null) {
+            return property;
+        }
+        else {
+            // assume this is a reference to an entity/view model, meaning we use an href
+            return refToHrefModel();
+        }
     }
 
     static String roSpecForResponseOf(final ObjectAction action) {
@@ -608,7 +734,15 @@ public class SwaggerSpecGenerator {
 
     static ArrayProperty arrayOfLinks() {
         return new ArrayProperty()
-                .items(new RefProperty("#/definitions/LinkModel"));
+                .items(refToLinkModel());
+    }
+
+    static RefProperty refToLinkModel() {
+        return new RefProperty("#/definitions/LinkModel");
+    }
+
+    static RefProperty refToHrefModel() {
+        return new RefProperty("#/definitions/HrefModel");
     }
 
     static ArrayProperty arrayOfStrings() {

http://git-wip-us.apache.org/repos/asf/isis/blob/4502dc10/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java
new file mode 100644
index 0000000..c00990e
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java
@@ -0,0 +1,174 @@
+/*
+ *  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 org.apache.isis.core.metamodel.services.swagger.internal;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Map;
+import java.util.UUID;
+
+import com.google.common.collect.Maps;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+
+import io.swagger.models.properties.BooleanProperty;
+import io.swagger.models.properties.ByteArrayProperty;
+import io.swagger.models.properties.DateProperty;
+import io.swagger.models.properties.DateTimeProperty;
+import io.swagger.models.properties.DecimalProperty;
+import io.swagger.models.properties.DoubleProperty;
+import io.swagger.models.properties.FloatProperty;
+import io.swagger.models.properties.IntegerProperty;
+import io.swagger.models.properties.LongProperty;
+import io.swagger.models.properties.Property;
+import io.swagger.models.properties.StringProperty;
+import io.swagger.models.properties.UUIDProperty;
+
+public class ValuePropertyFactory {
+
+    private final Map<Class, Factory> propertyFactoryByClass = Maps.newHashMap();
+
+    static interface Factory {
+        Property newProperty();
+    }
+    public ValuePropertyFactory() {
+
+        final Factory booleanFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new BooleanProperty();
+            }
+        };
+        propertyFactoryByClass.put(boolean.class, booleanFactory);
+        propertyFactoryByClass.put(Boolean.class, booleanFactory);
+
+        final Factory integerFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new IntegerProperty();
+            }
+        };
+        propertyFactoryByClass.put(byte.class, integerFactory);
+        propertyFactoryByClass.put(Byte.class, integerFactory);
+        propertyFactoryByClass.put(short.class, integerFactory);
+        propertyFactoryByClass.put(Short.class, integerFactory);
+        propertyFactoryByClass.put(int.class, integerFactory);
+        propertyFactoryByClass.put(Integer.class, integerFactory);
+        propertyFactoryByClass.put(BigInteger.class, integerFactory);
+
+        final Factory longFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new LongProperty();
+            }
+        };
+        propertyFactoryByClass.put(long.class, longFactory);
+        propertyFactoryByClass.put(Long.class, longFactory);
+        propertyFactoryByClass.put(java.sql.Timestamp.class, longFactory);
+
+        final Factory decimalFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new DecimalProperty();
+            }
+        };
+        propertyFactoryByClass.put(BigDecimal.class, decimalFactory);
+
+        final Factory floatFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new FloatProperty();
+            }
+        };
+        propertyFactoryByClass.put(float.class, floatFactory);
+        propertyFactoryByClass.put(Float.class, floatFactory);
+
+        final Factory doubleFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new DoubleProperty();
+            }
+        };
+        propertyFactoryByClass.put(double.class, doubleFactory);
+        propertyFactoryByClass.put(Double.class, doubleFactory);
+
+        final Factory stringFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new StringProperty();
+            }
+        };
+        propertyFactoryByClass.put(char[].class, stringFactory);
+        propertyFactoryByClass.put(String.class, stringFactory);
+
+        final Factory uuidFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new UUIDProperty();
+            }
+        };
+        propertyFactoryByClass.put(UUID.class, uuidFactory);
+
+        final Factory dateTimeFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new DateTimeProperty();
+            }
+        };
+        propertyFactoryByClass.put(java.util.Date.class, dateTimeFactory);
+        propertyFactoryByClass.put(DateTime.class, dateTimeFactory);
+        propertyFactoryByClass.put(LocalDateTime.class, dateTimeFactory);
+        propertyFactoryByClass.put(org.apache.isis.applib.value.DateTime.class, dateTimeFactory);
+
+        final Factory dateFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new DateProperty();
+            }
+        };
+        propertyFactoryByClass.put(java.sql.Date.class, dateFactory);
+        propertyFactoryByClass.put(LocalDate.class, dateFactory);
+        propertyFactoryByClass.put(org.apache.isis.applib.value.Date.class, dateFactory);
+
+        final Factory byteArrayFactory = new Factory() {
+            @Override
+            public Property newProperty() {
+                return new ByteArrayProperty();
+            }
+        };
+        propertyFactoryByClass.put(byte[].class, byteArrayFactory);
+        propertyFactoryByClass.put(org.apache.isis.applib.value.Blob.class, byteArrayFactory);
+    }
+
+    public Property newProperty(Class<?> cls) {
+        if(cls == null) {
+            return null;
+        }
+
+        final Factory factory = propertyFactoryByClass.get(cls);
+        if(factory != null) {
+            return factory.newProperty();
+        }
+
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/4502dc10/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java
----------------------------------------------------------------------
diff --git a/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java b/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java
index 98a485d..bee0af9 100644
--- a/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java
+++ b/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java
@@ -18,10 +18,14 @@
  */
 package domainapp.dom.simple;
 
+import java.util.List;
+
 import javax.jdo.JDOHelper;
 import javax.jdo.annotations.IdentityType;
 import javax.jdo.annotations.VersionStrategy;
 
+import com.google.common.collect.Lists;
+
 import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.BookmarkPolicy;
@@ -112,6 +116,11 @@ public class SimpleObject implements Comparable<SimpleObject> {
     }
 
 
+    public List<SimpleObject> getOthers() {
+        return Lists.newArrayList();
+    }
+
+
     /**
      * version (derived property)
      */

http://git-wip-us.apache.org/repos/asf/isis/blob/4502dc10/example/application/simpleapp/webapp/src/main/webapp/about/index.html
----------------------------------------------------------------------
diff --git a/example/application/simpleapp/webapp/src/main/webapp/about/index.html b/example/application/simpleapp/webapp/src/main/webapp/about/index.html
index b14a4b3..d243565 100644
--- a/example/application/simpleapp/webapp/src/main/webapp/about/index.html
+++ b/example/application/simpleapp/webapp/src/main/webapp/about/index.html
@@ -73,7 +73,7 @@ th, td {
                         <b><a href="wicket/">wicket/</a></b>
                     </p>
                     <p>
-                        provides accesses to a generic UI for end-users,
+                        provides access to a generic UI for end-users,
                         Isis' <a href="http://isis.apache.org/components/viewers/wicket/about.html" target="_blank">Wicket Viewer</a>.
                         As its name suggests, this viewer is built on top of <a href="http://wicket.apache.org" target="_blank">Apache Wicket</a>&trade;.
                     </p>
@@ -85,14 +85,17 @@ th, td {
                         </b>
                     </p>
                     <p>
-                        provides the Swagger UI access to RESTful API, consuming dynamically generated Swagger
-                        specification files.  There are three versions of this API: <a href="restful/swagger/public">public</a> (third-party) use, for <a href="restful/swagger/private">private</a> (internal) use, and extended version of the private API
-                        that includes <a href="restful/swagger/prototyping">prototyping</a> actions.
+                        provides access to a Swagger UI which uses a subset of the framework's automatically
+                        generated <a href="restful/">RESTful API</a>.  There are three versions of the Swagger
+                        spec files (that drive the UI): <a href="restful/swagger/public">public</a> (for use by
+                        "third-party" apps), <a href="restful/swagger/private">private</a> (for use by internal apps),
+                        and <a href="restful/swagger/prototyping">prototyping</a> (an extended version of the private
+                        API).  The Swagger spec files are themselves dynamically generated and can be used to generate
+                        client-side stubs.
                     </p>
-                    <p>The RESTful API can return both simple representations (suitable for use within custom apps),
-                        and also a richer hypermedia API conformant with the
-                        <a href="http://restfulobjects.org">Restful Objects</a> spec.  The HTTP <code>Accept</code> header
-                        is used to select which representation is required.
+                    <p>The RESTful API can return both simple representations and also richer hypermedia representations
+                        that are conformant with the <a href="http://restfulobjects.org">Restful Objects</a> spec.
+                        The HTTP <code>Accept</code> header is used to select which representation should be generated.
                     </p>
                 </li>
             </ul>