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; 020 021import java.io.InputStream; 022import java.util.Collections; 023import java.util.List; 024import java.util.Map; 025 026import javax.servlet.http.HttpServletRequest; 027import javax.servlet.http.HttpServletResponse; 028import javax.ws.rs.core.HttpHeaders; 029import javax.ws.rs.core.MediaType; 030import javax.ws.rs.core.Request; 031import javax.ws.rs.core.SecurityContext; 032import javax.ws.rs.core.UriInfo; 033 034import org.apache.isis.applib.annotation.Where; 035import org.apache.isis.applib.profiles.Localization; 036import org.apache.isis.core.commons.authentication.AuthenticationSession; 037import org.apache.isis.core.commons.config.IsisConfiguration; 038import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 039import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; 040import org.apache.isis.core.metamodel.spec.SpecificationLoader; 041import org.apache.isis.core.runtime.system.persistence.PersistenceSession; 042import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 043import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; 044import org.apache.isis.viewer.restfulobjects.applib.client.RestfulRequest.DomainModel; 045import org.apache.isis.viewer.restfulobjects.applib.client.RestfulRequest.RequestParameter; 046import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode; 047import org.apache.isis.viewer.restfulobjects.rendering.RendererContext; 048import org.apache.isis.viewer.restfulobjects.server.resources.DomainResourceHelper; 049 050import com.google.common.base.Predicate; 051import com.google.common.collect.Collections2; 052import com.google.common.collect.Iterables; 053import com.google.common.collect.Lists; 054 055public class ResourceContext implements RendererContext { 056 057 private final HttpHeaders httpHeaders; 058 private final UriInfo uriInfo; 059 private final Request request; 060 private final HttpServletRequest httpServletRequest; 061 private final HttpServletResponse httpServletResponse; 062 private final SecurityContext securityContext; 063 private final Localization localization; 064 065 @SuppressWarnings("unused") 066 private final IsisConfiguration configuration; 067 private final AuthenticationSession authenticationSession; 068 private final PersistenceSession persistenceSession; 069 private final AdapterManager adapterManager; 070 private final SpecificationLoader specificationLookup; 071 072 private List<List<String>> followLinks; 073 074 private final Where where; 075 private final String queryString; 076 private JsonRepresentation readQueryStringAsMap; 077 078 ////////////////////////////////////////////////////////////////// 079 // constructor and init 080 ////////////////////////////////////////////////////////////////// 081 082 public ResourceContext( 083 final RepresentationType representationType, 084 final HttpHeaders httpHeaders, 085 final UriInfo uriInfo, 086 final Request request, 087 final Where where, 088 final String queryStringIfAny, 089 final HttpServletRequest httpServletRequest, 090 final HttpServletResponse httpServletResponse, 091 final SecurityContext securityContext, 092 final Localization localization, final AuthenticationSession authenticationSession, 093 final PersistenceSession persistenceSession, 094 final AdapterManager objectAdapterLookup, 095 final SpecificationLoader specificationLookup, 096 final IsisConfiguration configuration) { 097 098 this.httpHeaders = httpHeaders; 099 this.uriInfo = uriInfo; 100 this.request = request; 101 this.queryString = queryStringIfAny; 102 this.httpServletRequest = httpServletRequest; 103 this.httpServletResponse = httpServletResponse; 104 this.securityContext = securityContext; 105 this.localization = localization; 106 this.configuration = configuration; 107 this.authenticationSession = authenticationSession; 108 this.persistenceSession = persistenceSession; 109 this.adapterManager = objectAdapterLookup; 110 this.specificationLookup = specificationLookup; 111 this.where = where; 112 113 init(representationType); 114 } 115 116 117 void init(final RepresentationType representationType) { 118 getQueryStringAsJsonRepr(); // force it to be cached 119 120 ensureCompatibleAcceptHeader(representationType); 121 ensureDomainModelQueryParamSupported(); 122 123 this.followLinks = Collections.unmodifiableList(getArg(RequestParameter.FOLLOW_LINKS)); 124 } 125 126 private void ensureDomainModelQueryParamSupported() { 127 final DomainModel domainModel = getArg(RequestParameter.DOMAIN_MODEL); 128 if(domainModel != DomainModel.FORMAL) { 129 throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, 130 "x-ro-domain-model of '%s' is not supported", domainModel); 131 } 132 } 133 134 private void ensureCompatibleAcceptHeader(final RepresentationType representationType) { 135 if (representationType == null) { 136 return; 137 } 138 139 // RestEasy will check the basic media types... 140 // ... so we just need to check the profile paramter 141 final String producedProfile = representationType.getMediaTypeProfile(); 142 if(producedProfile != null) { 143 for (MediaType mediaType : httpHeaders.getAcceptableMediaTypes()) { 144 String acceptedProfileValue = mediaType.getParameters().get("profile"); 145 if(acceptedProfileValue == null) { 146 continue; 147 } 148 if(!producedProfile.equals(acceptedProfileValue)) { 149 throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_ACCEPTABLE); 150 } 151 } 152 } 153 } 154 155 156 157 ////////////////////////////////////////////////////////////////// 158 // 159 ////////////////////////////////////////////////////////////////// 160 161 162 public HttpHeaders getHttpHeaders() { 163 return httpHeaders; 164 } 165 166 /** 167 * The {@link HttpServletRequest#getQueryString() query string}, cached 168 * after first call. 169 * 170 * <p> 171 * Note that this can return non-null for <tt>PUT</tt>s as well as <tt>GET</tt>s. 172 * It will only have been URL encoded for the latter; the caller should handle both cases. 173 */ 174 public String getQueryString() { 175 if(queryString != null) { 176 return queryString; 177 } 178 return getHttpServletRequest().getQueryString(); 179 } 180 181 public JsonRepresentation getQueryStringAsJsonRepr() { 182 183 if (readQueryStringAsMap == null) { 184 readQueryStringAsMap = requestArgsAsMap(); 185 } 186 return readQueryStringAsMap; 187 } 188 189 protected JsonRepresentation requestArgsAsMap() { 190 @SuppressWarnings("unchecked") 191 final Map<String,String[]> params = httpServletRequest.getParameterMap(); 192 193 if(simpleQueryArgs(params)) { 194 // try to process regular params and build up JSON repr 195 final JsonRepresentation map = JsonRepresentation.newMap(); 196 for(String paramName: params.keySet()) { 197 String paramValue = params.get(paramName)[0]; 198 try { 199 int paramValueAsInt = Integer.parseInt(paramValue); 200 map.mapPut(paramName+".value", paramValueAsInt); 201 } catch(Exception ex) { 202 map.mapPut(paramName+".value", paramValue); 203 } 204 } 205 return map; 206 } else { 207 return DomainResourceHelper.readQueryStringAsMap(getQueryString()); 208 } 209 } 210 211 private static boolean simpleQueryArgs(Map<String, String[]> params) { 212 if(params.isEmpty()) { 213 return false; 214 } 215 for(String paramName: params.keySet()) { 216 if("x-isis-querystring".equals(paramName) || paramName.startsWith("{")) { 217 return false; 218 } 219 } 220 return true; 221 } 222 223 224 public <Q> Q getArg(final RequestParameter<Q> requestParameter) { 225 final JsonRepresentation queryStringJsonRepr = getQueryStringAsJsonRepr(); 226 return requestParameter.valueOf(queryStringJsonRepr); 227 } 228 229 public UriInfo getUriInfo() { 230 return uriInfo; 231 } 232 233 public Request getRequest() { 234 return request; 235 } 236 237 public HttpServletRequest getHttpServletRequest() { 238 return httpServletRequest; 239 } 240 241 public HttpServletResponse getServletResponse() { 242 return httpServletResponse; 243 } 244 245 public SecurityContext getSecurityContext() { 246 return securityContext; 247 } 248 249 250 public List<List<String>> getFollowLinks() { 251 return followLinks; 252 } 253 254 255 256 public Localization getLocalization() { 257 return localization; 258 } 259 260 public AuthenticationSession getAuthenticationSession() { 261 return authenticationSession; 262 } 263 264 public AdapterManager getAdapterManager() { 265 return adapterManager; 266 } 267 268 public PersistenceSession getPersistenceSession() { 269 return persistenceSession; 270 } 271 272 public List<ObjectAdapter> getServiceAdapters() { 273 return persistenceSession.getServices(); 274 } 275 276 public SpecificationLoader getSpecificationLookup() { 277 return specificationLookup; 278 } 279 280 public Where getWhere() { 281 return where; 282 } 283 284 285 ////////////////////////////////////////////////////////////////// 286 // 287 ////////////////////////////////////////////////////////////////// 288 289 public String urlFor(final String url) { 290 return getUriInfo().getBaseUri().toString() + url; 291 } 292 293}