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 @Override 239 public <T> List<T> getParameters(String name) { 240 if (StringUtils.isEmpty(name)) { 241 return null; 242 } 243 String realName = name.toLowerCase().trim(); 244 @SuppressWarnings("unchecked") 245 List<T> values = (List<T>) parameters.get(realName); 246 if (values != null) { 247 return new ArrayList<T>(values); 248 } else { 249 return new ArrayList<T>(); 250 } 251 } 252 253 @Override 254 public Map<String, List<Object>> getAllParameters() { 255 // make a copy of the local parameters 256 Map<String, List<Object>> unModifiableParameters = new HashMap<String, List<Object>>(); 257 for (Map.Entry<String, List<Object>> entry : parameters.entrySet()) { 258 String key = entry.getKey(); 259 List<Object> value = entry.getValue(); 260 if (value == null) { 261 unModifiableParameters.put(key, null); 262 } else { 263 unModifiableParameters.put(key, new ArrayList<Object>(value)); 264 } 265 } 266 return unModifiableParameters; 267 } 268 269 @Override 270 public void setParameterValues(String name, Object... values) { 271 if (StringUtils.isEmpty(name)) { 272 return; 273 } 274 String realName = name.toLowerCase().trim(); 275 if (values.length == 0) { 276 parameters.remove(realName); 277 return; 278 } 279 setParameterListValues(realName, Arrays.asList(values)); 280 } 281 282 @Override 283 public void setParameterListValues(String name, List<Object> values) { 284 if (StringUtils.isEmpty(name)) { 285 return; 286 } 287 String realName = name.toLowerCase().trim(); 288 if (values == null) { 289 parameters.remove(realName); 290 } 291 parameters.put(realName, new CopyOnWriteArrayList<Object>(values)); 292 } 293 294 @Override 295 public void addParameterValues(String name, Object... values) { 296 addParameterListValues(name, Arrays.asList(values)); 297 } 298 299 @Override 300 public void addParameterListValues(String name, List<Object> values) { 301 if (StringUtils.isEmpty(name)) { 302 return; 303 } 304 String realName = name.toLowerCase().trim(); 305 if (values == null) { 306 return; 307 } 308 List<Object> currentValues = parameters.get(realName); 309 if (currentValues == null) { 310 currentValues = new CopyOnWriteArrayList<Object>(); 311 parameters.put(realName, currentValues); 312 } 313 for (Object value : values) { 314 currentValues.add(value); 315 } 316 } 317 318 static RenderingContextBuilder builder() { 319 return new RenderingContextBuilder(); 320 } 321 322 public static final class RenderingContextBuilder { 323 324 private RenderingContextImpl ctx; 325 326 RenderingContextBuilder() { 327 ctx = new RenderingContextImpl(); 328 } 329 330 public RenderingContextBuilder base(String url) { 331 ctx.baseUrl = url; 332 return this; 333 } 334 335 public RenderingContextBuilder locale(Locale locale) { 336 ctx.locale = locale; 337 return this; 338 } 339 340 public RenderingContextBuilder session(CoreSession session) { 341 ctx.session = session; 342 return this; 343 } 344 345 public RenderingContextBuilder param(String name, Object value) { 346 ctx.addParameterValues(name, value); 347 return this; 348 } 349 350 public RenderingContextBuilder paramValues(String name, Object... values) { 351 ctx.addParameterValues(name, values); 352 return this; 353 } 354 355 public RenderingContextBuilder paramList(String name, List<Object> values) { 356 ctx.addParameterListValues(name, values); 357 return this; 358 } 359 360 public RenderingContextBuilder properties(String... schemaName) { 361 return paramValues(EMBED_PROPERTIES, (Object[]) schemaName); 362 } 363 364 public RenderingContextBuilder enrich(String entityType, String... enricherName) { 365 return paramValues(EMBED_ENRICHERS + SEPARATOR + entityType, (Object[]) enricherName); 366 } 367 368 public RenderingContextBuilder enrichDoc(String... enricherName) { 369 return enrich(ENTITY_TYPE, enricherName); 370 } 371 372 public RenderingContextBuilder fetch(String entityType, String... propertyName) { 373 return paramValues(FETCH_PROPERTIES + SEPARATOR + entityType, (Object[]) propertyName); 374 } 375 376 public RenderingContextBuilder fetchInDoc(String... propertyName) { 377 return fetch(ENTITY_TYPE, propertyName); 378 } 379 380 public RenderingContextBuilder translate(String entityType, String... propertyName) { 381 return paramValues(TRANSLATE_PROPERTIES + SEPARATOR + entityType, (Object[]) propertyName); 382 } 383 384 public RenderingContextBuilder depth(DepthValues value) { 385 ctx.setParameterValues(MarshallingConstants.MAX_DEPTH_PARAM, value.name()); 386 return this; 387 } 388 389 public RenderingContext get() { 390 return ctx; 391 } 392 393 } 394 395}