001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.isis.viewer.restfulobjects.server.resources; 020 021import java.util.Collection; 022 023import javax.ws.rs.GET; 024import javax.ws.rs.Path; 025import javax.ws.rs.PathParam; 026import javax.ws.rs.Produces; 027import javax.ws.rs.QueryParam; 028import javax.ws.rs.core.MediaType; 029import javax.ws.rs.core.Response; 030 031import com.google.common.base.Strings; 032 033import org.apache.isis.applib.annotation.Where; 034import org.apache.isis.core.metamodel.spec.ObjectSpecId; 035import org.apache.isis.core.metamodel.spec.ObjectSpecification; 036import org.apache.isis.core.metamodel.spec.feature.ObjectAction; 037import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter; 038import org.apache.isis.core.metamodel.spec.feature.ObjectMember; 039import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; 040import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; 041import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 042import org.apache.isis.viewer.restfulobjects.applib.Rel; 043import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; 044import org.apache.isis.viewer.restfulobjects.applib.RestfulMediaType; 045import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode; 046import org.apache.isis.viewer.restfulobjects.applib.domaintypes.DomainTypeResource; 047import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder; 048import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ActionDescriptionReprRenderer; 049import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ActionParameterDescriptionReprRenderer; 050import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.CollectionDescriptionReprRenderer; 051import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.DomainTypeReprRenderer; 052import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ParentSpecAndAction; 053import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ParentSpecAndActionParam; 054import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ParentSpecAndCollection; 055import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ParentSpecAndProperty; 056import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.PropertyDescriptionReprRenderer; 057import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.TypeActionResultReprRenderer; 058import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.TypeListReprRenderer; 059import org.apache.isis.viewer.restfulobjects.server.RestfulObjectsApplicationException; 060import org.apache.isis.viewer.restfulobjects.server.util.UrlParserUtils; 061 062/** 063 * Implementation note: it seems to be necessary to annotate the implementation 064 * with {@link Path} rather than the interface (at least under RestEasy 1.0.2 065 * and 1.1-RC2). 066 */ 067@Path("/domain-types") 068public class DomainTypeResourceServerside extends ResourceAbstract implements DomainTypeResource { 069 070 @Override 071 @GET 072 @Path("/") 073 @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_TYPE_LIST }) 074 public Response domainTypes() { 075 final RepresentationType representationType = RepresentationType.TYPE_LIST; 076 init(representationType, Where.ANYWHERE); 077 078 final Collection<ObjectSpecification> allSpecifications = getSpecificationLoader().allSpecifications(); 079 080 final TypeListReprRenderer renderer = new TypeListReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 081 renderer.with(allSpecifications).includesSelf(); 082 083 return responseOfOk(renderer, Caching.ONE_DAY).build(); 084 } 085 086 @Override 087 @GET 088 @Path("/{domainType}") 089 @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_DOMAIN_TYPE }) 090 public Response domainType(@PathParam("domainType") final String domainType) { 091 092 init(RepresentationType.DOMAIN_TYPE, Where.ANYWHERE); 093 094 final ObjectSpecification objectSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType)); 095 096 final DomainTypeReprRenderer renderer = new DomainTypeReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 097 renderer.with(objectSpec).includesSelf(); 098 099 return responseOfOk(renderer, Caching.ONE_DAY).build(); 100 } 101 102 @Override 103 @GET 104 @Path("/{domainType}/properties/{propertyId}") 105 @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_PROPERTY_DESCRIPTION }) 106 public Response typeProperty(@PathParam("domainType") final String domainType, @PathParam("propertyId") final String propertyId) { 107 final RepresentationType representationType = RepresentationType.PROPERTY_DESCRIPTION; 108 init(representationType, Where.ANYWHERE); 109 110 final ObjectSpecification parentSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType)); 111 if (parentSpec == null) { 112 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND); 113 } 114 115 final ObjectMember objectMember = parentSpec.getAssociation(propertyId); 116 if (objectMember == null || objectMember.isOneToManyAssociation()) { 117 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND); 118 } 119 final OneToOneAssociation property = (OneToOneAssociation) objectMember; 120 121 final PropertyDescriptionReprRenderer renderer = new PropertyDescriptionReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 122 renderer.with(new ParentSpecAndProperty(parentSpec, property)).includesSelf(); 123 124 return responseOfOk(renderer, Caching.ONE_DAY).build(); 125 } 126 127 @Override 128 @GET 129 @Path("/{domainType}/collections/{collectionId}") 130 @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_COLLECTION_DESCRIPTION }) 131 public Response typeCollection(@PathParam("domainType") final String domainType, @PathParam("collectionId") final String collectionId) { 132 final RepresentationType representationType = RepresentationType.COLLECTION_DESCRIPTION; 133 init(representationType, Where.ANYWHERE); 134 135 final ObjectSpecification parentSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType)); 136 if (parentSpec == null) { 137 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND); 138 } 139 140 final ObjectMember objectMember = parentSpec.getAssociation(collectionId); 141 if (objectMember == null || objectMember.isOneToOneAssociation()) { 142 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND); 143 } 144 final OneToManyAssociation collection = (OneToManyAssociation) objectMember; 145 146 final CollectionDescriptionReprRenderer renderer = new CollectionDescriptionReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 147 renderer.with(new ParentSpecAndCollection(parentSpec, collection)).includesSelf(); 148 149 return responseOfOk(renderer, Caching.ONE_DAY).build(); 150 } 151 152 @Override 153 @GET 154 @Path("/{domainType}/actions/{actionId}") 155 @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_ACTION_DESCRIPTION }) 156 public Response typeAction(@PathParam("domainType") final String domainType, @PathParam("actionId") final String actionId) { 157 final RepresentationType representationType = RepresentationType.ACTION_DESCRIPTION; 158 init(representationType, Where.ANYWHERE); 159 160 final ObjectSpecification parentSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType)); 161 if (parentSpec == null) { 162 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND); 163 } 164 165 final ObjectMember objectMember = parentSpec.getObjectAction(actionId); 166 if (objectMember == null) { 167 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND); 168 } 169 final ObjectAction action = (ObjectAction) objectMember; 170 171 final ActionDescriptionReprRenderer renderer = new ActionDescriptionReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 172 renderer.with(new ParentSpecAndAction(parentSpec, action)).includesSelf(); 173 174 return responseOfOk(renderer, Caching.ONE_DAY).build(); 175 } 176 177 @Override 178 @GET 179 @Path("/{domainType}/actions/{actionId}/params/{paramName}") 180 @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_ACTION_PARAMETER_DESCRIPTION }) 181 public Response typeActionParam(@PathParam("domainType") final String domainType, @PathParam("actionId") final String actionId, @PathParam("paramName") final String paramName) { 182 final RepresentationType representationType = RepresentationType.ACTION_PARAMETER_DESCRIPTION; 183 init(representationType, Where.ANYWHERE); 184 185 final ObjectSpecification parentSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType)); 186 if (parentSpec == null) { 187 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND); 188 } 189 190 final ObjectMember objectMember = parentSpec.getObjectAction(actionId); 191 if (objectMember == null) { 192 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND); 193 } 194 final ObjectAction parentAction = (ObjectAction) objectMember; 195 196 final ObjectActionParameter actionParam = parentAction.getParameterByName(paramName); 197 198 final ActionParameterDescriptionReprRenderer renderer = new ActionParameterDescriptionReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 199 renderer.with(new ParentSpecAndActionParam(parentSpec, actionParam)).includesSelf(); 200 201 return responseOfOk(renderer, Caching.ONE_DAY).build(); 202 } 203 204 // ////////////////////////////////////////////////////////// 205 // domain type actions 206 // ////////////////////////////////////////////////////////// 207 208 @Override 209 @GET 210 @Path("/{domainType}/type-actions/isSubtypeOf/invoke") 211 @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_TYPE_ACTION_RESULT, RestfulMediaType.APPLICATION_JSON_ERROR }) 212 public Response domainTypeIsSubtypeOf( 213 @PathParam("domainType") final String domainType, 214 @QueryParam("supertype") final String superTypeStr, // simple style 215 @QueryParam("args") final String args // formal style 216 ) { 217 init(Where.ANYWHERE); 218 219 final String supertype = domainTypeFor(superTypeStr, args, "supertype"); 220 221 final ObjectSpecification domainTypeSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType)); 222 final ObjectSpecification supertypeSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(supertype)); 223 224 final TypeActionResultReprRenderer renderer = new TypeActionResultReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 225 226 final String url = "domain-types/" + domainType + "/type-actions/isSubtypeOf/invoke"; 227 final LinkBuilder linkBuilder = LinkBuilder.newBuilder(getResourceContext(), Rel.SELF.getName(), RepresentationType.TYPE_ACTION_RESULT, url); 228 final JsonRepresentation arguments = DomainTypeReprRenderer.argumentsTo(getResourceContext(), "supertype", supertypeSpec); 229 final JsonRepresentation selfLink = linkBuilder.withArguments(arguments).build(); 230 231 final boolean value = domainTypeSpec.isOfType(supertypeSpec); 232 renderer.with(domainTypeSpec).withSelf(selfLink).withValue(value); 233 234 return responseOfOk(renderer, Caching.ONE_DAY).build(); 235 } 236 237 238 @Override 239 @GET 240 @Path("/{domainType}/type-actions/isSupertypeOf/invoke") 241 @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_TYPE_ACTION_RESULT, RestfulMediaType.APPLICATION_JSON_ERROR }) 242 public Response domainTypeIsSupertypeOf( 243 @PathParam("domainType") final String domainType, 244 @QueryParam("subtype") final String subTypeStr, // simple style 245 @QueryParam("args") final String args // formal style 246 ) { 247 248 init(Where.ANYWHERE); 249 250 final String subtype = domainTypeFor(subTypeStr, args, "subtype"); 251 252 final ObjectSpecification domainTypeSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType)); 253 final ObjectSpecification subtypeSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(subtype)); 254 255 final TypeActionResultReprRenderer renderer = new TypeActionResultReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 256 257 final String url = "domain-types/" + domainType + "/type-actions/isSupertypeOf/invoke"; 258 final LinkBuilder linkBuilder = LinkBuilder.newBuilder(getResourceContext(), Rel.SELF.getName(), RepresentationType.TYPE_ACTION_RESULT, url); 259 final JsonRepresentation arguments = DomainTypeReprRenderer.argumentsTo(getResourceContext(), "subtype", subtypeSpec); 260 final JsonRepresentation selfLink = linkBuilder.withArguments(arguments).build(); 261 262 final boolean value = subtypeSpec.isOfType(domainTypeSpec); 263 renderer.with(domainTypeSpec).withSelf(selfLink).withValue(value); 264 265 return responseOfOk(renderer, Caching.ONE_DAY).build(); 266 } 267 268 private static String domainTypeFor(final String domainTypeStr, final String argumentsQueryString, final String argsParamName) { 269 // simple style; simple return 270 if (!Strings.isNullOrEmpty(domainTypeStr)) { 271 return domainTypeStr; 272 } 273 274 // formal style; must parse from args that has a link with an href to 275 // the domain type 276 final String href = linkFromFormalArgs(argumentsQueryString, argsParamName); 277 return UrlParserUtils.domainTypeFrom(href); 278 } 279 280 private static String linkFromFormalArgs(final String argumentsQueryString, final String paramName) { 281 final JsonRepresentation arguments = DomainResourceHelper.readQueryStringAsMap(argumentsQueryString); 282 if (!arguments.isLink(paramName)) { 283 throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Args should contain a link '%s'", paramName); 284 } 285 286 return arguments.getLink(paramName).getHref(); 287 } 288 289}