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.io.IOException; 022import java.io.InputStream; 023import java.text.DateFormat; 024import java.text.SimpleDateFormat; 025import java.util.Date; 026import java.util.List; 027import java.util.TimeZone; 028 029import javax.servlet.http.HttpServletRequest; 030import javax.servlet.http.HttpServletResponse; 031import javax.ws.rs.core.CacheControl; 032import javax.ws.rs.core.Context; 033import javax.ws.rs.core.EntityTag; 034import javax.ws.rs.core.HttpHeaders; 035import javax.ws.rs.core.MediaType; 036import javax.ws.rs.core.Request; 037import javax.ws.rs.core.Response; 038import javax.ws.rs.core.Response.ResponseBuilder; 039import javax.ws.rs.core.SecurityContext; 040import javax.ws.rs.core.UriInfo; 041 042import org.apache.isis.applib.annotation.Where; 043import org.apache.isis.applib.profiles.Localization; 044import org.apache.isis.core.commons.authentication.AuthenticationSession; 045import org.apache.isis.core.commons.config.IsisConfiguration; 046import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 047import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; 048import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller; 049import org.apache.isis.core.metamodel.adapter.version.Version; 050import org.apache.isis.core.metamodel.services.ServiceUtil; 051import org.apache.isis.core.metamodel.spec.ActionType; 052import org.apache.isis.core.metamodel.spec.ObjectSpecification; 053import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi; 054import org.apache.isis.core.runtime.persistence.ObjectNotFoundException; 055import org.apache.isis.core.runtime.system.context.IsisContext; 056import org.apache.isis.core.runtime.system.persistence.OidGenerator; 057import org.apache.isis.core.runtime.system.persistence.PersistenceSession; 058import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; 059import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode; 060import org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper; 061import org.apache.isis.viewer.restfulobjects.rendering.ReprRenderer; 062import org.apache.isis.viewer.restfulobjects.server.ResourceContext; 063import org.apache.isis.viewer.restfulobjects.server.RestfulObjectsApplicationException; 064import org.apache.isis.viewer.restfulobjects.server.util.OidUtils; 065import org.apache.isis.viewer.restfulobjects.server.util.UrlDecoderUtils; 066import org.codehaus.jackson.JsonGenerationException; 067import org.codehaus.jackson.map.JsonMappingException; 068 069public abstract class ResourceAbstract { 070 071 072 protected final static JsonMapper jsonMapper = JsonMapper.instance(); 073 074 public enum Caching { 075 ONE_DAY(24 * 60 * 60), ONE_HOUR(60 * 60), NONE(0); 076 077 private final CacheControl cacheControl; 078 079 private Caching(final int maxAge) { 080 this.cacheControl = new CacheControl(); 081 if (maxAge > 0) { 082 cacheControl.setMaxAge(maxAge); 083 } else { 084 cacheControl.setNoCache(true); 085 } 086 } 087 088 public CacheControl getCacheControl() { 089 return cacheControl; 090 } 091 } 092 093 // nb: SET is excluded; we simply flatten contributed actions. 094 public final static ActionType[] ACTION_TYPES = { ActionType.USER, ActionType.DEBUG, ActionType.EXPLORATION }; 095 096 private final static String UTC_DATEFORMAT = "yyyy-MM-ddTHH:mm:ss.sss"; 097 098 @Context 099 HttpHeaders httpHeaders; 100 101 @Context 102 UriInfo uriInfo; 103 104 @Context 105 Request request; 106 107 @Context 108 HttpServletRequest httpServletRequest; 109 110 @Context 111 HttpServletResponse httpServletResponse; 112 113 @Context 114 SecurityContext securityContext; 115 116 private ResourceContext resourceContext; 117 118 protected void init(Where where) { 119 init(RepresentationType.GENERIC, where); 120 } 121 122 protected void init(final RepresentationType representationType, Where where) { 123 init(representationType, where, (String)null); 124 } 125 126 protected void init(RepresentationType representationType, Where where, InputStream arguments) { 127 final String queryString = DomainResourceHelper.asStringUtf8(arguments); 128 init(representationType, where, queryString); 129 } 130 131 protected void init(RepresentationType representationType, Where where, String queryString) { 132 if (!IsisContext.inSession()) { 133 throw RestfulObjectsApplicationException.create(HttpStatusCode.UNAUTHORIZED); 134 } 135 if (getAuthenticationSession() == null) { 136 throw RestfulObjectsApplicationException.create(HttpStatusCode.UNAUTHORIZED); 137 } 138 139 this.resourceContext = new ResourceContext( 140 representationType, httpHeaders, uriInfo, request, where, queryString, httpServletRequest, httpServletResponse, 141 securityContext, getLocalization(), getAuthenticationSession(), getPersistenceSession(), getAdapterManager(), getSpecificationLoader(), getConfiguration()); 142 } 143 144 protected ResourceContext getResourceContext() { 145 return resourceContext; 146 } 147 148 // ////////////////////////////////////////////////////////////// 149 // Rendering 150 // ////////////////////////////////////////////////////////////// 151 152 protected static String jsonFor(final Object object) { 153 try { 154 return jsonMapper.write(object); 155 } catch (final JsonGenerationException e) { 156 throw new RuntimeException(e); 157 } catch (final JsonMappingException e) { 158 throw new RuntimeException(e); 159 } catch (final IOException e) { 160 throw new RuntimeException(e); 161 } 162 } 163 164 // ////////////////////////////////////////////////////////////// 165 // Isis integration 166 // ////////////////////////////////////////////////////////////// 167 168 protected ObjectSpecification getSpecification(final String specFullName) { 169 return getSpecificationLoader().loadSpecification(specFullName); 170 } 171 172 protected ObjectAdapter getObjectAdapterElseThrowNotFound(String domainType, final String instanceId) { 173 ObjectAdapter objectAdapter = getObjectAdapterElseNull(domainType, instanceId); 174 175 if (objectAdapter == null) { 176 final String instanceIdUnencoded = UrlDecoderUtils.urlDecode(instanceId); 177 throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_FOUND, "could not determine adapter for OID: '%s:%s'", domainType, instanceIdUnencoded); 178 } 179 return objectAdapter; 180 } 181 182 protected ObjectAdapter getObjectAdapterElseNull(String domainType, final String instanceId) { 183 return OidUtils.getObjectAdapterElseNull(resourceContext, domainType, instanceId); 184 } 185 186 protected ObjectAdapter getServiceAdapter(final String serviceId) { 187 final List<ObjectAdapter> serviceAdapters = getPersistenceSession().getServices(); 188 for (final ObjectAdapter serviceAdapter : serviceAdapters) { 189 final Object servicePojo = serviceAdapter.getObject(); 190 final String id = ServiceUtil.id(servicePojo); 191 if (serviceId.equals(id)) { 192 return serviceAdapter; 193 } 194 } 195 throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_FOUND, "Could not locate service '%s'", serviceId); 196 } 197 198 199 // ////////////////////////////////////////////////////////////// 200 // Responses 201 // ////////////////////////////////////////////////////////////// 202 203 public static ResponseBuilder responseOfNoContent() { 204 return responseOf(HttpStatusCode.NO_CONTENT); 205 } 206 207 public static ResponseBuilder responseOfOk(final ReprRenderer<?, ?> renderer, final Caching caching) { 208 return responseOfOk(renderer, caching, null); 209 } 210 211 public static ResponseBuilder responseOfOk(final ReprRenderer<?, ?> renderer, final Caching caching, final Version version) { 212 final MediaType mediaType = renderer.getMediaType(); 213 final ResponseBuilder response = responseOf(HttpStatusCode.OK).type(mediaType).cacheControl(caching.getCacheControl()).entity(jsonFor(renderer.render())); 214 return addLastModifiedAndETagIfAvailable(response, version); 215 } 216 217 protected static ResponseBuilder responseOf(final HttpStatusCode httpStatusCode) { 218 return Response.status(httpStatusCode.getJaxrsStatusType()).type(MediaType.APPLICATION_JSON_TYPE); 219 } 220 221 public static ResponseBuilder addLastModifiedAndETagIfAvailable(final ResponseBuilder responseBuilder, final Version version) { 222 if (version != null && version.getTime() != null) { 223 final Date time = version.getTime(); 224 responseBuilder.lastModified(time); 225 responseBuilder.tag(asETag(time)); 226 } 227 return responseBuilder; 228 } 229 230 private static EntityTag asETag(final Date time) { 231 final SimpleDateFormat sdf = new SimpleDateFormat(UTC_DATEFORMAT); 232 sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 233 final String utcTime = sdf.format(time); 234 return new EntityTag(utcTime, true); 235 } 236 237 // ////////////////////////////////////////////////////////////// 238 // Dependencies (from singletons) 239 // ////////////////////////////////////////////////////////////// 240 241 protected IsisConfiguration getConfiguration () { 242 return IsisContext.getConfiguration(); 243 } 244 245 protected AuthenticationSession getAuthenticationSession() { 246 return IsisContext.getAuthenticationSession(); 247 } 248 249 protected SpecificationLoaderSpi getSpecificationLoader() { 250 return IsisContext.getSpecificationLoader(); 251 } 252 253 protected AdapterManager getAdapterManager() { 254 return getPersistenceSession().getAdapterManager(); 255 } 256 257 protected PersistenceSession getPersistenceSession() { 258 return IsisContext.getPersistenceSession(); 259 } 260 261 protected Localization getLocalization() { 262 return IsisContext.getLocalization(); 263 } 264 265 protected OidMarshaller getOidMarshaller() { 266 return IsisContext.getOidMarshaller(); 267 } 268 269 // ////////////////////////////////////////////////////////////// 270 // Dependencies (injected via @Context) 271 // ////////////////////////////////////////////////////////////// 272 273 protected HttpServletRequest getServletRequest() { 274 return getResourceContext().getHttpServletRequest(); 275 } 276 277}