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 2018/02/28 12:38:32 UTC

[isis] 01/06: ISIS-1882: adds DomainObjectList, special cases support within JaxbServiceDefault, adds support for new Accept type for action invocation.

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch maint-1.16.2
in repository https://gitbox.apache.org/repos/asf/isis.git

commit d6fddaddfa53d7386e67e7dfff47ffadf8cc6959
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Feb 28 08:59:25 2018 +0000

    ISIS-1882: adds DomainObjectList, special cases support within JaxbServiceDefault, adds support for new Accept type for action invocation.
---
 .../applib/services/jaxb/DomainObjectList.java     |  59 ++++++++++
 .../isis/applib/services/jaxb/JaxbService.java     |  54 +++++++--
 .../schema/services/jaxb/JaxbServiceDefault.java   |  19 ++--
 .../jaxbadapters/PersistentEntitiesAdapter.java    |  70 ++++++++++++
 .../services/swagger/internal/Generation.java      |   1 +
 ...entNegotiationServiceForRestfulObjectsV1_0.java | 122 ++++++++++++++++++---
 6 files changed, 294 insertions(+), 31 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/jaxb/DomainObjectList.java b/core/applib/src/main/java/org/apache/isis/applib/services/jaxb/DomainObjectList.java
new file mode 100644
index 0000000..b30ad3c
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/jaxb/DomainObjectList.java
@@ -0,0 +1,59 @@
+package org.apache.isis.applib.services.jaxb;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.annotation.Collection;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.schema.utils.jaxbadapters.PersistentEntitiesAdapter;
+
+@XmlRootElement(name = "list")
+@XmlType(
+        propOrder = {
+                "title",
+                "elementType",
+                "objects"
+        }
+)
+@XmlAccessorType(XmlAccessType.FIELD)
+public class DomainObjectList {
+
+    public DomainObjectList() {
+    }
+    public DomainObjectList(final String title, final Class<?> elementType) {
+        this.title = title;
+        this.elementType = elementType.getCanonicalName();
+    }
+
+    private String title;
+    public String title() { return title; }
+
+    private String elementType;
+    @Property(editing = Editing.DISABLED)
+    public String getElementType() {
+        return elementType;
+    }
+
+
+    @XmlJavaTypeAdapter(PersistentEntitiesAdapter.class)
+    private List<Object> objects = Lists.newArrayList();
+
+    @Collection(editing = Editing.DISABLED)
+    public List<Object> getObjects() {
+        return objects;
+    }
+
+    public void setObjects(final List<Object> objects) {
+        this.objects = objects;
+    }
+
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/jaxb/JaxbService.java b/core/applib/src/main/java/org/apache/isis/applib/services/jaxb/JaxbService.java
index a0d50d5..b028893 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/jaxb/JaxbService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/jaxb/JaxbService.java
@@ -109,15 +109,16 @@ public interface JaxbService {
         public Object fromXml(final JAXBContext jaxbContext, final String xml, final Map<String, Object> unmarshallerProperties) {
             try {
 
-                final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+                Object unmarshal = internalFromXml(jaxbContext, xml, unmarshallerProperties);
 
-                for (Map.Entry<String, Object> entry : unmarshallerProperties.entrySet()) {
-                    unmarshaller.setProperty(entry.getKey(), entry.getValue());
-                }
+                if(unmarshal instanceof DomainObjectList) {
 
-                configure(unmarshaller);
+                    // go around the loop again, so can properly deserialize the contents
+                    DomainObjectList domainObjectList = (DomainObjectList) unmarshal;
+                    JAXBContext jaxbContext1 = jaxbContextFor(domainObjectList);
+                    unmarshal = internalFromXml(jaxbContext1, xml, unmarshallerProperties);
+                }
 
-                final Object unmarshal = unmarshaller.unmarshal(new StringReader(xml));
                 return unmarshal;
 
             } catch (final JAXBException ex) {
@@ -125,17 +126,33 @@ public interface JaxbService {
             }
         }
 
+        private Object internalFromXml(
+                final JAXBContext jaxbContext,
+                final String xml,
+                final Map<String, Object> unmarshallerProperties) throws JAXBException {
+            final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+
+            for (Map.Entry<String, Object> entry : unmarshallerProperties.entrySet()) {
+                unmarshaller.setProperty(entry.getKey(), entry.getValue());
+            }
+
+            configure(unmarshaller);
+
+            return unmarshaller.unmarshal(new StringReader(xml));
+        }
+
         @Override
         public <T> T fromXml(final Class<T> domainClass, final String xml) {
             return fromXml(domainClass, xml, Maps.<String,Object>newHashMap());
         }
+
         @Override
         public <T> T fromXml(final Class<T> domainClass, final String xml, final Map<String, Object> unmarshallerProperties) {
             final JAXBContext context = jaxbContextFor(domainClass);
             return (T) fromXml(context, xml, unmarshallerProperties);
         }
 
-        private <T> JAXBContext jaxbContextFor(final Class<T> clazz)  {
+        private static <T> JAXBContext jaxbContextFor(final Class<T> clazz)  {
             try {
                 return JaxbUtil.jaxbContextFor(clazz);
             } catch (RuntimeException e) {
@@ -152,9 +169,9 @@ public interface JaxbService {
         public String toXml(final Object domainObject, final Map<String, Object> marshallerProperties)  {
 
             final Class<?> domainClass = domainObject.getClass();
-            try {
-                final JAXBContext context = jaxbContextFor(domainClass);
+            final JAXBContext context = jaxbContextFor(domainObject);
 
+            try {
                 final Marshaller marshaller = context.createMarshaller();
 
                 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
@@ -205,6 +222,24 @@ public interface JaxbService {
             }
         }
 
+        private static JAXBContext jaxbContextFor(final Object domainObject) {
+            final Class<?> domainClass = domainObject.getClass();
+            final JAXBContext context;
+            if(domainObject instanceof DomainObjectList) {
+                DomainObjectList list = (DomainObjectList) domainObject;
+                final Class<?> elementType;
+                try {
+                    elementType = Thread.currentThread().getContextClassLoader().loadClass(list.getElementType());
+                    context = JAXBContext.newInstance(domainClass, elementType);
+                } catch (JAXBException | ClassNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+            } else {
+                context = jaxbContextFor(domainClass);
+            }
+            return context;
+        }
+
         /**
          * Optional hook
          */
@@ -233,5 +268,4 @@ public interface JaxbService {
         }
     }
 
-
 }
\ No newline at end of file
diff --git a/core/applib/src/main/java/org/apache/isis/schema/services/jaxb/JaxbServiceDefault.java b/core/applib/src/main/java/org/apache/isis/schema/services/jaxb/JaxbServiceDefault.java
index a6072b8..0537d10 100644
--- a/core/applib/src/main/java/org/apache/isis/schema/services/jaxb/JaxbServiceDefault.java
+++ b/core/applib/src/main/java/org/apache/isis/schema/services/jaxb/JaxbServiceDefault.java
@@ -19,10 +19,11 @@ package org.apache.isis.schema.services.jaxb;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
 
-import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.services.jaxb.JaxbService;
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.schema.utils.jaxbadapters.PersistentEntitiesAdapter;
 import org.apache.isis.schema.utils.jaxbadapters.PersistentEntityAdapter;
 
 @DomainService(
@@ -32,18 +33,20 @@ import org.apache.isis.schema.utils.jaxbadapters.PersistentEntityAdapter;
 public class JaxbServiceDefault extends JaxbService.Simple {
 
     protected void configure(final Unmarshaller unmarshaller) {
-        final PersistentEntityAdapter adapter = new PersistentEntityAdapter();
-        container.injectServicesInto(adapter);
-        unmarshaller.setAdapter(PersistentEntityAdapter.class, adapter);
+        unmarshaller.setAdapter(PersistentEntityAdapter.class,
+                serviceRegistry.injectServicesInto(new PersistentEntityAdapter()));
+        unmarshaller.setAdapter(PersistentEntitiesAdapter.class,
+                serviceRegistry.injectServicesInto(new PersistentEntitiesAdapter()));
     }
 
     protected void configure(final Marshaller marshaller) {
-        final PersistentEntityAdapter adapter = new PersistentEntityAdapter();
-        container.injectServicesInto(adapter);
-        marshaller.setAdapter(PersistentEntityAdapter.class, adapter);
+        marshaller.setAdapter(PersistentEntityAdapter.class,
+                serviceRegistry.injectServicesInto(new PersistentEntityAdapter()));
+        marshaller.setAdapter(PersistentEntitiesAdapter.class,
+                serviceRegistry.injectServicesInto(new PersistentEntitiesAdapter()));
     }
 
     @javax.inject.Inject
-    DomainObjectContainer container;
+    ServiceRegistry serviceRegistry;
 }
 
diff --git a/core/applib/src/main/java/org/apache/isis/schema/utils/jaxbadapters/PersistentEntitiesAdapter.java b/core/applib/src/main/java/org/apache/isis/schema/utils/jaxbadapters/PersistentEntitiesAdapter.java
new file mode 100644
index 0000000..a09c56b
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/schema/utils/jaxbadapters/PersistentEntitiesAdapter.java
@@ -0,0 +1,70 @@
+/**
+ *  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.schema.utils.jaxbadapters;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.bookmark.BookmarkService2;
+import org.apache.isis.schema.common.v1.OidDto;
+import org.apache.isis.schema.common.v1.OidsDto;
+
+public class PersistentEntitiesAdapter extends XmlAdapter<OidsDto, List<Object>> {
+
+    @Override
+    public List<Object> unmarshal(final OidsDto oidsDto) throws Exception {
+
+        List<Object> domainObjects = Lists.newArrayList();
+        for (final OidDto oidDto : oidsDto.getOid()) {
+            final Bookmark bookmark = Bookmark.from(oidDto);
+            Object domainObject = bookmarkService.lookup(bookmark, BookmarkService2.FieldResetPolicy.DONT_RESET);
+            domainObjects.add(domainObject);
+        }
+        return domainObjects;
+    }
+
+    @Override
+    public OidsDto marshal(final List<Object> domainObjects) throws Exception {
+        if(domainObjects == null) {
+            return null;
+        }
+        OidsDto oidsDto = new OidsDto();
+        for (final Object domainObject : domainObjects) {
+            final Bookmark bookmark = getBookmarkService().bookmarkFor(domainObject);
+            oidsDto.getOid().add(bookmark.toOidDto());
+        }
+        return oidsDto;
+    }
+
+    private static String coalesce(final String first, final String second) {
+        return first != null? first: second;
+    }
+
+
+    protected BookmarkService getBookmarkService() {
+        return bookmarkService;
+    }
+
+    @Inject
+    BookmarkService2 bookmarkService;
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
index 604624b..caf6d38 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
@@ -460,6 +460,7 @@ class Generation {
                         .description(Util.roSpec("19.1") + ": (invoke) resource of " + serviceId + "#" + actionId)
                         .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")
                         .produces("application/json;profile=urn:org.restfulobjects:repr-types/action-result")
                 ;
 
diff --git a/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java b/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
index fb666a8..dc124c6 100644
--- a/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
+++ b/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
@@ -18,6 +18,7 @@ package org.apache.isis.viewer.restfulobjects.rendering.service.conneg;
 
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -29,8 +30,12 @@ import javax.ws.rs.core.Response.ResponseBuilder;
 
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.services.jaxb.DomainObjectList;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.version.Version;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
 import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
@@ -199,15 +204,74 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
             final RepresentationService.Context2 rendererContext,
             final ObjectAndActionInvocation objectAndActionInvocation) {
 
-        final List<MediaType> list = rendererContext.getAcceptableMediaTypes();
-        ensureCompatibleAcceptHeader(RepresentationType.ACTION_RESULT, list);
+        final ResponseBuilder responseBuilder;
 
-        final ResponseBuilder responseBuilder =
-                buildResponseTo(rendererContext, objectAndActionInvocation, JsonRepresentation.newMap(), null);
+        final List<MediaType> acceptableMediaTypes = rendererContext.getAcceptableMediaTypes();
+        if(isAccepted(RepresentationType.DOMAIN_OBJECT, acceptableMediaTypes, true)) {
+
+            final Collection<ObjectAdapter> collectionAdapters = objectAdaptersFrom(objectAndActionInvocation);
+            final ObjectSpecification elementSpec = elementSpecFrom(objectAndActionInvocation);
+            final DomainObjectList list = domainObjectListFrom(collectionAdapters, elementSpec);
+
+            final ObjectAdapter adapter = rendererContext.getPersistenceSession().adapterFor(list);
+            responseBuilder = buildResponse(rendererContext, adapter);
+
+        } else if(isAccepted(RepresentationType.ACTION_RESULT, acceptableMediaTypes)) {
+            responseBuilder = buildResponseTo(rendererContext, objectAndActionInvocation, JsonRepresentation.newMap(), null);
+        } else {
+            throw RestfulObjectsApplicationException.create(RestfulResponse.HttpStatusCode.NOT_ACCEPTABLE);
+        }
 
         return responseBuilder(responseBuilder);
     }
 
+    private static DomainObjectList domainObjectListFrom(
+            final Collection<ObjectAdapter> collectionAdapters,
+            final ObjectSpecification elementSpec) {
+
+        final String title = titleFrom(collectionAdapters, elementSpec);
+
+        final DomainObjectList list = new DomainObjectList(title, elementSpec.getCorrespondingClass());
+        for (final ObjectAdapter adapter : collectionAdapters) {
+            list.getObjects().add(adapter.getObject());
+        }
+        return list;
+    }
+
+    private static String titleFrom(
+            final Collection<ObjectAdapter> collectionAdapters,
+            final ObjectSpecification elementSpec) {
+        final String singularName = elementSpec.getSingularName();
+        final String pluralName = elementSpec.getPluralName();
+        int size = collectionAdapters.size();
+        final String title;
+        switch (size) {
+        case 0:
+            title = "0 " + pluralName;
+            break;
+        case 1:
+            title = "1 " + singularName;
+            break;
+        default:
+            title = size + " " + pluralName;
+            break;
+        }
+        return title;
+    }
+
+    private ObjectSpecification elementSpecFrom(final ObjectAndActionInvocation objectAndActionInvocation) {
+        final TypeOfFacet typeOfFacet = objectAndActionInvocation.getAction().getFacet(TypeOfFacet.class);
+        return typeOfFacet.valueSpec();
+    }
+
+    private Collection<ObjectAdapter> objectAdaptersFrom(final ObjectAndActionInvocation objectAndActionInvocation) {
+        final ObjectAdapter returnedAdapter = objectAndActionInvocation.getReturnedAdapter();
+        final ObjectSpecification returnType = objectAndActionInvocation.getAction().getReturnType();
+
+        final CollectionFacet collectionFacet = returnType.getFacet(CollectionFacet.class);
+        return collectionFacet.collection(returnedAdapter);
+    }
+
     /**
      * Not API
      */
@@ -248,18 +312,50 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
         // RestEasy will check the basic media types...
         // ... so we just need to check the profile paramter
         final String producedProfile = representationType.getMediaTypeProfile();
-        if(producedProfile != null) {
-            for (MediaType mediaType : acceptableMediaTypes ) {
-                String acceptedProfileValue = mediaType.getParameters().get("profile");
-                if(acceptedProfileValue == null) {
-                    continue;
-                }
-                if(!producedProfile.equals(acceptedProfileValue)) {
-                    throw RestfulObjectsApplicationException.create(RestfulResponse.HttpStatusCode.NOT_ACCEPTABLE);
-                }
+        if (producedProfile == null) {
+            return;
+        }
+        boolean accepted = isAccepted(producedProfile, acceptableMediaTypes);
+        if(!accepted) {
+            throw RestfulObjectsApplicationException.create(RestfulResponse.HttpStatusCode.NOT_ACCEPTABLE);
+        }
+    }
+
+    private boolean isAccepted(
+            final RepresentationType representationType,
+            final List<MediaType> acceptableMediaTypes) {
+        return isAccepted(representationType, acceptableMediaTypes, strictAcceptChecking);
+    }
+
+    private boolean isAccepted(
+            final RepresentationType representationType,
+            final List<MediaType> acceptableMediaTypes,
+            final boolean strictAcceptChecking) {
+        if(!strictAcceptChecking) {
+            return true;
+        }
+        final String producedProfile = representationType.getMediaTypeProfile();
+        if (producedProfile == null) {
+            throw new IllegalArgumentException("RepresentationType " + representationType + " does not specify a 'profile' parameter");
+        }
+        return isAccepted(producedProfile, acceptableMediaTypes);
+    }
+
+    private static boolean isAccepted(
+            final String producedProfile,
+            final List<MediaType> acceptableMediaTypes) {
+        for (MediaType mediaType : acceptableMediaTypes ) {
+            String acceptedProfileValue = mediaType.getParameters().get("profile");
+            if(acceptedProfileValue == null) {
+                continue;
+            }
+            if(!producedProfile.equals(acceptedProfileValue)) {
+                return false;
             }
         }
+        return true;
     }
 
 
+
 }

-- 
To stop receiving notification emails like this one, please contact
danhaywood@apache.org.