001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nicolas Chapurlat <nchapurlat@nuxeo.com> 018 */ 019 020/* 021 import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.EMBED_PROPERTIES; 022 import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.HEADER_PREFIX; 023 024 import java.util.Arrays; 025 import java.util.Collections; 026 import java.util.List; 027 import java.util.Locale; 028 import java.util.Map; 029 import java.util.Set; 030 import java.util.TreeSet; 031 import java.util.concurrent.ConcurrentHashMap; 032 import java.util.concurrent.CopyOnWriteArrayList; 033 034 import org.nuxeo.common.utils.StringUtils; 035 import org.nuxeo.ecm.core.io.registry.MarshallingConstants; 036 */ 037 038package org.nuxeo.ecm.core.io.registry.context; 039 040import static org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter.ENTITY_TYPE; 041import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.EMBED_ENRICHERS; 042import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.EMBED_PROPERTIES; 043import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.FETCH_PROPERTIES; 044import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.HEADER_PREFIX; 045import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.SEPARATOR; 046import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.TRANSLATE_PROPERTIES; 047import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.WRAPPED_CONTEXT; 048 049import java.util.ArrayList; 050import java.util.Arrays; 051import java.util.Collections; 052import java.util.HashMap; 053import java.util.List; 054import java.util.Locale; 055import java.util.Map; 056import java.util.Set; 057import java.util.TreeSet; 058import java.util.concurrent.ConcurrentHashMap; 059import java.util.concurrent.CopyOnWriteArrayList; 060 061import org.apache.commons.lang.StringUtils; 062import org.nuxeo.ecm.core.api.CoreInstance; 063import org.nuxeo.ecm.core.api.CoreSession; 064import org.nuxeo.ecm.core.api.DocumentModel; 065import org.nuxeo.ecm.core.io.registry.MarshallingConstants; 066import org.nuxeo.ecm.core.io.registry.MarshallingException; 067 068/** 069 * A thread-safe {@link RenderingContext} implementation. Please use {@link RenderingContext.CtxBuilder} to create 070 * instance of {@link RenderingContext}. 071 * 072 * @since 7.2 073 */ 074public class RenderingContextImpl implements RenderingContext { 075 076 private String baseUrl = DEFAULT_URL; 077 078 private Locale locale = DEFAULT_LOCALE; 079 080 private CoreSession session = null; 081 082 private final Map<String, List<Object>> parameters = new ConcurrentHashMap<String, List<Object>>(); 083 084 private RenderingContextImpl() { 085 } 086 087 @Override 088 public Locale getLocale() { 089 return locale; 090 } 091 092 @Override 093 public String getBaseUrl() { 094 return baseUrl; 095 } 096 097 @Override 098 public SessionWrapper getSession(DocumentModel document) { 099 if (document != null) { 100 CoreSession docSession = null; 101 try { 102 docSession = document.getCoreSession(); 103 } catch (UnsupportedOperationException e) { 104 // do nothing 105 } 106 if (docSession != null) { 107 return new SessionWrapper(docSession, false); 108 } 109 } 110 if (session != null) { 111 return new SessionWrapper(session, false); 112 } 113 String repoNameFound = getParameter("X-NXRepository"); 114 if (StringUtils.isBlank(repoNameFound)) { 115 repoNameFound = getParameter("nxrepository"); 116 if (StringUtils.isBlank(repoNameFound)) { 117 try { 118 repoNameFound = document.getRepositoryName(); 119 } catch (UnsupportedOperationException e) { 120 // do nothing 121 } 122 } 123 } 124 if (!StringUtils.isBlank(repoNameFound)) { 125 CoreSession session = CoreInstance.openCoreSession(repoNameFound); 126 return new SessionWrapper(session, true); 127 } 128 throw new MarshallingException("Unable to create a new session"); 129 } 130 131 @Override 132 public void setExistingSession(CoreSession session) { 133 this.session = session; 134 } 135 136 @Override 137 public Set<String> getProperties() { 138 return getSplittedParameterValues(EMBED_PROPERTIES); 139 } 140 141 @Override 142 public Set<String> getFetched(String entity) { 143 return getSplittedParameterValues(FETCH_PROPERTIES, entity); 144 } 145 146 @Override 147 public Set<String> getTranslated(String entity) { 148 return getSplittedParameterValues(TRANSLATE_PROPERTIES, entity); 149 } 150 151 @Override 152 public Set<String> getEnrichers(String entity) { 153 return getSplittedParameterValues(EMBED_ENRICHERS, entity); 154 } 155 156 private Set<String> getSplittedParameterValues(String category, String... subCategories) { 157 // supports dot '.' as separator 158 Set<String> result = getSplittedParameterValues('.', category, subCategories); 159 // supports hyphen '-' as separator 160 result.addAll(getSplittedParameterValues(SEPARATOR, category, subCategories)); 161 return result; 162 } 163 164 @SuppressWarnings("deprecation") 165 private Set<String> getSplittedParameterValues(char separator, String category, String... subCategories) { 166 if (category == null) { 167 return Collections.emptySet(); 168 } 169 String paramKey = category; 170 for (String subCategory : subCategories) { 171 paramKey += separator + subCategory; 172 } 173 paramKey = paramKey.toLowerCase(); 174 List<Object> dirty = getParameters(paramKey); 175 dirty.addAll(getParameters(HEADER_PREFIX + paramKey)); 176 // backward compatibility, supports X-NXDocumentProperties and X-NXContext-Category 177 if (EMBED_PROPERTIES.toLowerCase().equals(paramKey)) { 178 dirty.addAll(getParameters(MarshallingConstants.DOCUMENT_PROPERTIES_HEADER)); 179 } else if ((EMBED_ENRICHERS + separator + ENTITY_TYPE).toLowerCase().equals(paramKey)) { 180 dirty.addAll(getParameters(MarshallingConstants.NXCONTENT_CATEGORY_HEADER)); 181 } 182 Set<String> result = new TreeSet<String>(); 183 for (Object value : dirty) { 184 if (value instanceof String && value != null) { 185 for (String cleaned : org.nuxeo.common.utils.StringUtils.split((String) value, ',', true)) { 186 result.add(cleaned); 187 } 188 } 189 } 190 return result; 191 } 192 193 private <T> T getWrappedEntity(String name) { 194 return WrappedContext.getEntity(this, name); 195 } 196 197 @Override 198 public WrappedContext wrap() { 199 return WrappedContext.create(this); 200 } 201 202 @Override 203 public <T> T getParameter(String name) { 204 if (StringUtils.isEmpty(name)) { 205 return null; 206 } 207 String realName = name.toLowerCase().trim(); 208 List<Object> values = parameters.get(realName); 209 if (values != null && values.size() > 0) { 210 @SuppressWarnings("unchecked") 211 T value = (T) values.get(0); 212 return value; 213 } 214 if (WRAPPED_CONTEXT.toLowerCase().equals(realName)) { 215 return null; 216 } else { 217 return getWrappedEntity(realName); 218 } 219 } 220 221 @Override 222 public boolean getBooleanParameter(String name) { 223 Object result = getParameter(name); 224 if (result == null) { 225 return false; 226 } else if (result instanceof Boolean) { 227 return (Boolean) result; 228 } else if (result instanceof String) { 229 try { 230 return Boolean.valueOf((String) result); 231 } catch (Exception e) { 232 return false; 233 } 234 } 235 return false; 236 } 237 238 239 @SuppressWarnings({ "rawtypes", "unchecked" }) 240 @Override 241 public <T> List<T> getParameters(String name) { 242 if (StringUtils.isEmpty(name)) { 243 return null; 244 } 245 String realName = name.toLowerCase().trim(); 246 List<T> values = (List<T>) parameters.get(realName); 247 List<T> result; 248 if (values != null) { 249 result = new ArrayList<T>(values); 250 } else { 251 result = new ArrayList<T>(); 252 } 253 if (WRAPPED_CONTEXT.toLowerCase().equals(realName)) { 254 return result; 255 } else { 256 Object wrapped = getWrappedEntity(realName); 257 if (wrapped == null) { 258 return result; 259 } 260 if (wrapped instanceof List) { 261 for (Object element: (List) wrapped) { 262 try { 263 T casted = (T) element; 264 result.add(casted); 265 } catch (ClassCastException e) { 266 return null; 267 } 268 } 269 } else { 270 try { 271 T casted = (T) wrapped; 272 result.add(casted); 273 } catch (ClassCastException e) { 274 return null; 275 } 276 } 277 } 278 return result; 279 } 280 281 @Override 282 public Map<String, List<Object>> getAllParameters() { 283 // make a copy of the local parameters 284 Map<String, List<Object>> unModifiableParameters = new HashMap<String, List<Object>>(); 285 for (Map.Entry<String, List<Object>> entry : parameters.entrySet()) { 286 String key = entry.getKey(); 287 List<Object> value = entry.getValue(); 288 if (value == null) { 289 unModifiableParameters.put(key, null); 290 } else { 291 unModifiableParameters.put(key, new ArrayList<Object>(value)); 292 } 293 } 294 return unModifiableParameters; 295 } 296 297 @Override 298 public void setParameterValues(String name, Object... values) { 299 if (StringUtils.isEmpty(name)) { 300 return; 301 } 302 String realName = name.toLowerCase().trim(); 303 if (values.length == 0) { 304 parameters.remove(realName); 305 return; 306 } 307 setParameterListValues(realName, Arrays.asList(values)); 308 } 309 310 @Override 311 public void setParameterListValues(String name, List<Object> values) { 312 if (StringUtils.isEmpty(name)) { 313 return; 314 } 315 String realName = name.toLowerCase().trim(); 316 if (values == null) { 317 parameters.remove(realName); 318 } 319 parameters.put(realName, new CopyOnWriteArrayList<Object>(values)); 320 } 321 322 @Override 323 public void addParameterValues(String name, Object... values) { 324 addParameterListValues(name, Arrays.asList(values)); 325 } 326 327 @Override 328 public void addParameterListValues(String name, List<Object> values) { 329 if (StringUtils.isEmpty(name)) { 330 return; 331 } 332 String realName = name.toLowerCase().trim(); 333 if (values == null) { 334 return; 335 } 336 List<Object> currentValues = parameters.get(realName); 337 if (currentValues == null) { 338 currentValues = new CopyOnWriteArrayList<Object>(); 339 parameters.put(realName, currentValues); 340 } 341 for (Object value : values) { 342 currentValues.add(value); 343 } 344 } 345 346 static RenderingContextBuilder builder() { 347 return new RenderingContextBuilder(); 348 } 349 350 public static final class RenderingContextBuilder { 351 352 private RenderingContextImpl ctx; 353 354 RenderingContextBuilder() { 355 ctx = new RenderingContextImpl(); 356 } 357 358 public RenderingContextBuilder base(String url) { 359 ctx.baseUrl = url; 360 return this; 361 } 362 363 public RenderingContextBuilder locale(Locale locale) { 364 ctx.locale = locale; 365 return this; 366 } 367 368 public RenderingContextBuilder session(CoreSession session) { 369 ctx.session = session; 370 return this; 371 } 372 373 public RenderingContextBuilder param(String name, Object value) { 374 ctx.addParameterValues(name, value); 375 return this; 376 } 377 378 public RenderingContextBuilder paramValues(String name, Object... values) { 379 ctx.addParameterValues(name, values); 380 return this; 381 } 382 383 public RenderingContextBuilder paramList(String name, List<Object> values) { 384 ctx.addParameterListValues(name, values); 385 return this; 386 } 387 388 public RenderingContextBuilder properties(String... schemaName) { 389 return paramValues(EMBED_PROPERTIES, (Object[]) schemaName); 390 } 391 392 public RenderingContextBuilder enrich(String entityType, String... enricherName) { 393 return paramValues(EMBED_ENRICHERS + SEPARATOR + entityType, (Object[]) enricherName); 394 } 395 396 public RenderingContextBuilder enrichDoc(String... enricherName) { 397 return enrich(ENTITY_TYPE, enricherName); 398 } 399 400 public RenderingContextBuilder fetch(String entityType, String... propertyName) { 401 return paramValues(FETCH_PROPERTIES + SEPARATOR + entityType, (Object[]) propertyName); 402 } 403 404 public RenderingContextBuilder fetchInDoc(String... propertyName) { 405 return fetch(ENTITY_TYPE, propertyName); 406 } 407 408 public RenderingContextBuilder translate(String entityType, String... propertyName) { 409 return paramValues(TRANSLATE_PROPERTIES + SEPARATOR + entityType, (Object[]) propertyName); 410 } 411 412 public RenderingContextBuilder depth(DepthValues value) { 413 ctx.setParameterValues(MarshallingConstants.MAX_DEPTH_PARAM, value.name()); 414 return this; 415 } 416 417 public RenderingContext get() { 418 return ctx; 419 } 420 421 } 422 423}