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.