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 08:59:49 UTC

[isis] branch ISIS-1882 created (now 25842db)

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

danhaywood pushed a change to branch ISIS-1882
in repository https://gitbox.apache.org/repos/asf/isis.git.


      at 25842db  ISIS-1882: adds DomainObjectList, special cases support within JaxbServiceDefault, adds support for new Accept type for action invocation.

This branch includes the following new commits:

     new 25842db  ISIS-1882: adds DomainObjectList, special cases support within JaxbServiceDefault, adds support for new Accept type for action invocation.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


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

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

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 25842dbe15a20be0ccd2d12f9fb101d7abf31b79
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.