You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/05/22 16:56:48 UTC
[isis] branch master updated: ISIS-2686: don't use
RepresentationType.DOMAIN_OBJECT if type to be rendered does not fit
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new bc4bb16 ISIS-2686: don't use RepresentationType.DOMAIN_OBJECT if type to be rendered does not fit
bc4bb16 is described below
commit bc4bb1608ebcbd741a8608ac5f2c07246b2871a5
Author: ahuber@apache.org <ah...@luna>
AuthorDate: Sat May 22 18:55:58 2021 +0200
ISIS-2686: don't use RepresentationType.DOMAIN_OBJECT if type to be
rendered does not fit
---
.../metamodel/spec/ManagedObjectInternalUtil.java | 9 ++
.../domainobjects/ObjectAndActionInvocation.java | 8 +
...entNegotiationServiceForRestfulObjectsV1_0.java | 163 ++++++++++++---------
3 files changed, 107 insertions(+), 73 deletions(-)
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjectInternalUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjectInternalUtil.java
index 2e8f9ed..f28917e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjectInternalUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjectInternalUtil.java
@@ -33,11 +33,13 @@ import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
import lombok.NonNull;
import lombok.val;
import lombok.experimental.UtilityClass;
+import lombok.extern.log4j.Log4j2;
/**
* @since 2.0
*/
@UtilityClass
+@Log4j2
final class ManagedObjectInternalUtil {
@@ -72,6 +74,13 @@ final class ManagedObjectInternalUtil {
}
static Optional<Bookmark> bookmark(final @Nullable ManagedObject adapter) {
+
+ if(!ManagedObjects.isIdentifiable(adapter)
+ && ManagedObjects.isSpecified(adapter)) {
+ log.warn("about to create a random UUID bookmark for {}; this is probably an invalid code-path taken (TODO)",
+ adapter.getSpecification());
+ }
+
return objectManager(adapter)
.map(objectManager->objectManager.bookmarkObject(adapter));
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectAndActionInvocation.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectAndActionInvocation.java
index 7cfbca1..99de803 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectAndActionInvocation.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectAndActionInvocation.java
@@ -109,6 +109,14 @@ public class ObjectAndActionInvocation {
return !elementAdapters.get().isEmpty();
}
+ /**
+ * Returns the ObjectSpecification of the compile time return type of the associated action.
+ * (not inspecting the runtime type)
+ */
+ public ObjectSpecification getReturnTypeSpecification() {
+ return getAction().getReturnType();
+ }
+
// -- HELPER
private final _Lazy<Can<ManagedObject>> elementAdapters = _Lazy.threadSafe(this::initElementAdapters);
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
index d838804..62392e2 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceForRestfulObjectsV1_0.java
@@ -59,6 +59,7 @@ import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectColle
import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectPropertyReprRenderer;
import org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationService;
+import lombok.NonNull;
import lombok.val;
/**
@@ -78,7 +79,7 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
protected final IsisConfiguration configuration;
protected final SpecificationLoader specificationLoader;
- private final boolean strictAcceptChecking;
+ private final AcceptChecking acceptChecking;
@Inject
public ContentNegotiationServiceForRestfulObjectsV1_0(
@@ -86,7 +87,7 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
final SpecificationLoader specificationLoader) {
this.configuration = configuration;
this.specificationLoader = specificationLoader;
- this.strictAcceptChecking = configuration.getViewer().getRestfulobjects().isStrictAcceptChecking();
+ this.acceptChecking = AcceptChecking.fromConfig(configuration);
}
@Override
@@ -94,13 +95,10 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
final IResourceContext resourceContext,
final ManagedObject objectAdapter) {
- final List<MediaType> list = resourceContext.getAcceptableMediaTypes();
- ensureCompatibleAcceptHeader(RepresentationType.DOMAIN_OBJECT, list);
+ ensureCompatibleAcceptHeader(RepresentationType.DOMAIN_OBJECT, resourceContext);
- final ResponseBuilder responseBuilder = buildResponseTo(
- resourceContext, objectAdapter, JsonRepresentation.newMap(), null);
-
- return responseBuilder(responseBuilder);
+ return responseBuilder(buildResponseTo(
+ resourceContext, objectAdapter, JsonRepresentation.newMap(), null));
}
/**
@@ -113,16 +111,14 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
final JsonRepresentation rootRepresentation) {
final DomainObjectReprRenderer renderer =
- new DomainObjectReprRenderer(resourceContext, null, representationIfAnyRequired);
- renderer.with(objectAdapter).includesSelf();
+ new DomainObjectReprRenderer(resourceContext, null, representationIfAnyRequired)
+ .with(objectAdapter)
+ .includesSelf();
final ResponseBuilder responseBuilder = Responses.ofOk(renderer, Caching.NONE, rootRepresentation);
- {
- final RepresentationService.Intent intent = resourceContext.getIntent();
- if(intent == RepresentationService.Intent.JUST_CREATED) {
- responseBuilder.status(Response.Status.CREATED);
- }
+ if(resourceContext.getIntent() == RepresentationService.Intent.JUST_CREATED) {
+ responseBuilder.status(Response.Status.CREATED);
}
return responseBuilder;
@@ -133,20 +129,19 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
final IResourceContext resourceContext,
final ManagedProperty objectAndProperty) {
- final List<MediaType> list = resourceContext.getAcceptableMediaTypes();
- ensureCompatibleAcceptHeader(RepresentationType.OBJECT_PROPERTY, list);
+ ensureCompatibleAcceptHeader(RepresentationType.OBJECT_PROPERTY, resourceContext);
- final ObjectPropertyReprRenderer renderer = new ObjectPropertyReprRenderer(resourceContext);
- renderer.with(objectAndProperty)
- .usingLinkTo(resourceContext.getObjectAdapterLinkTo());
+ final ObjectPropertyReprRenderer renderer =
+ new ObjectPropertyReprRenderer(resourceContext)
+ .with(objectAndProperty)
+ .usingLinkTo(resourceContext.getObjectAdapterLinkTo());
val repMode = objectAndProperty.getRepresentationMode();
if(repMode.isExplicit()) {
renderer.withMemberMode(repMode);
}
- final ResponseBuilder responseBuilder = Responses.ofOk(renderer, Caching.NONE);
- return responseBuilder;
+ return Responses.ofOk(renderer, Caching.NONE);
}
@Override
@@ -154,13 +149,10 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
final IResourceContext resourceContext,
final ManagedCollection objectAndCollection) {
- final List<MediaType> list = resourceContext.getAcceptableMediaTypes();
- ensureCompatibleAcceptHeader(RepresentationType.OBJECT_COLLECTION, list);
-
- final ResponseBuilder responseBuilder =
- buildResponseTo(resourceContext, objectAndCollection, JsonRepresentation.newMap(), null);
+ ensureCompatibleAcceptHeader(RepresentationType.OBJECT_COLLECTION, resourceContext);
- return responseBuilder(responseBuilder);
+ return responseBuilder(buildResponseTo(
+ resourceContext, objectAndCollection, JsonRepresentation.newMap(), null));
}
/**
@@ -188,17 +180,15 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
final IResourceContext resourceContext,
final ManagedAction objectAndAction) {
- final List<MediaType> list = resourceContext.getAcceptableMediaTypes();
- ensureCompatibleAcceptHeader(RepresentationType.OBJECT_ACTION, list);
+ ensureCompatibleAcceptHeader(RepresentationType.OBJECT_ACTION, resourceContext);
- final ObjectActionReprRenderer renderer = new ObjectActionReprRenderer(resourceContext);
- renderer.with(objectAndAction)
- .usingLinkTo(resourceContext.getObjectAdapterLinkTo())
- .asStandalone();
+ final ObjectActionReprRenderer renderer =
+ new ObjectActionReprRenderer(resourceContext)
+ .with(objectAndAction)
+ .usingLinkTo(resourceContext.getObjectAdapterLinkTo())
+ .asStandalone();
- final ResponseBuilder responseBuilder = Responses.ofOk(renderer, Caching.NONE);
-
- return responseBuilder(responseBuilder);
+ return responseBuilder(Responses.ofOk(renderer, Caching.NONE));
}
@Override
@@ -206,12 +196,15 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
final IResourceContext resourceContext,
final ObjectAndActionInvocation objectAndActionInvocation) {
- final ResponseBuilder responseBuilder;
+ val returnTypeCompileTimeSpecification = objectAndActionInvocation.getReturnTypeSpecification();
+
+ val isDomainObjectOrCollection = returnTypeCompileTimeSpecification.isEntityOrViewModelOrAbstract()
+ || returnTypeCompileTimeSpecification.isParentedOrFreeCollection();
final List<MediaType> acceptableMediaTypes = resourceContext.getAcceptableMediaTypes();
- if(isAccepted(RepresentationType.DOMAIN_OBJECT, acceptableMediaTypes, true)) {
+ if(isDomainObjectOrCollection
+ && isAccepted(RepresentationType.DOMAIN_OBJECT, acceptableMediaTypes)) {
- final ManagedObject adapter;
final Collection<ManagedObject> collectionAdapters = objectAdaptersFrom(objectAndActionInvocation);
if(collectionAdapters != null) {
@@ -221,22 +214,45 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
final String actionArguments = actionArgumentsFrom(objectAndActionInvocation);
final DomainObjectList list = domainObjectListFrom(collectionAdapters, elementSpec, actionOwnerSpec, actionId, actionArguments);
- adapter = ManagedObject.lazy(resourceContext.getSpecificationLoader(), list);
+ val listAdapter = ManagedObject.lazy(resourceContext.getSpecificationLoader(), list);
+ return responseBuilder(
+ buildResponse(
+ resourceContext,
+ listAdapter));
} else {
- adapter = objectAndActionInvocation.getReturnedAdapter();
+
+ return responseBuilder(
+ buildResponse(
+ resourceContext,
+ objectAndActionInvocation.getReturnedAdapter()));
}
- responseBuilder = buildResponse(resourceContext, adapter);
- } else if(isAccepted(RepresentationType.ACTION_RESULT, acceptableMediaTypes)) {
- responseBuilder = buildResponseTo(resourceContext, objectAndActionInvocation, JsonRepresentation.newMap(), null);
- } else {
- throw RestfulObjectsApplicationException.create(RestfulResponse.HttpStatusCode.NOT_ACCEPTABLE);
}
- return responseBuilder(responseBuilder);
+ if(isAccepted(RepresentationType.ACTION_RESULT, acceptableMediaTypes)) {
+
+ return responseBuilder(
+ buildResponseTo(
+ resourceContext,
+ objectAndActionInvocation,
+ JsonRepresentation.newMap(),
+ /*rootRepr*/null));
+ }
+
+ throw RestfulObjectsApplicationException.create(RestfulResponse.HttpStatusCode.NOT_ACCEPTABLE);
+
}
+ /**
+ * For easy sub-classing to further customize, eg additional headers
+ */
+ protected ResponseBuilder responseBuilder(final ResponseBuilder responseBuilder) {
+ return responseBuilder;
+ }
+
+ // -- HELPER
+
private static ObjectSpecification actionOwnerSpecFrom(final ObjectAndActionInvocation objectAndActionInvocation) {
return objectAndActionInvocation.getAction().getOnType();
}
@@ -348,19 +364,25 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
return responseBuilder;
}
- /**
- * For easy subclassing to further customize, eg additional headers
- */
- protected ResponseBuilder responseBuilder(final ResponseBuilder responseBuilder) {
- return responseBuilder;
+ private static enum AcceptChecking {
+ RELAXED,
+ /**
+ * Any unrecognized Accept headers will result in an HTTP Not Acceptable Response code (406).
+ */
+ STRICT;
+ static AcceptChecking fromConfig(IsisConfiguration configuration) {
+ return configuration.getViewer().getRestfulobjects().isStrictAcceptChecking()
+ ? AcceptChecking.STRICT
+ : AcceptChecking.RELAXED;
+ }
+ boolean isStrict() { return this == STRICT; }
+ boolean isRelaxed() { return this == RELAXED; }
}
-
-
private void ensureCompatibleAcceptHeader(
final RepresentationType representationType,
- final List<MediaType> acceptableMediaTypes) {
- if(!strictAcceptChecking) {
+ final IResourceContext resourceContext) {
+ if(acceptChecking.isRelaxed()) {
return;
}
if (representationType == null) {
@@ -368,13 +390,12 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
}
// RestEasy will check the basic media types...
- // ... so we just need to check the profile paramter
+ // ... so we just need to check the profile parameter
final String producedProfile = representationType.getMediaTypeProfile();
if (producedProfile == null) {
return;
}
- boolean accepted = isAccepted(producedProfile, acceptableMediaTypes);
- if(!accepted) {
+ if(!isAccepted(producedProfile, resourceContext.getAcceptableMediaTypes())) {
throw RestfulObjectsApplicationException.create(RestfulResponse.HttpStatusCode.NOT_ACCEPTABLE);
}
}
@@ -382,26 +403,22 @@ public class ContentNegotiationServiceForRestfulObjectsV1_0 implements ContentNe
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");
+ if (producedProfile != null) {
+ return isAccepted(producedProfile, acceptableMediaTypes);
}
- return isAccepted(producedProfile, acceptableMediaTypes);
+ if(acceptChecking.isStrict()) {
+ throw new IllegalArgumentException("RepresentationType " + representationType
+ + " does not specify a 'profile' parameter");
+ }
+ return false;
}
private static boolean isAccepted(
- final String producedProfile,
+ final @NonNull String producedProfile,
final List<MediaType> acceptableMediaTypes) {
+
for (MediaType mediaType : acceptableMediaTypes ) {
String acceptedProfileValue = mediaType.getParameters().get("profile");
if(acceptedProfileValue == null) {