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