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
020package org.nuxeo.ecm.core.io.registry.context;
021
022import java.io.Closeable;
023import java.io.IOException;
024import java.util.List;
025import java.util.Locale;
026import java.util.Map;
027import java.util.Set;
028
029import org.nuxeo.ecm.core.api.CloseableCoreSession;
030import org.nuxeo.ecm.core.api.CoreSession;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.core.io.registry.MarshallingException;
033import org.nuxeo.ecm.core.io.registry.context.RenderingContextImpl.RenderingContextBuilder;
034
035/**
036 * A context used to deliver parameter to marshallers during a marshalling request.
037 * <p>
038 * Use {@link CtxBuilder} to create your context.
039 * </p>
040 * <p>
041 * When a {@link RenderingContext} is automatically provided from an HttpServletRequest, it contains request parameters,
042 * headers and request attribute.
043 * </p>
044 * <p>
045 * To get/set parameter values, use:
046 * <ul>
047 * <li>{@link RenderingContext#getParameter(String)}</li>
048 * <li>{@link RenderingContext#getParameters(String)}</li>
049 * <li>{@link RenderingContext#getBooleanParameter(String)}</li>
050 * <li>{@link RenderingContext#getAllParameters()}</li>
051 * <li>{@link RenderingContext#addParameterValues(String, Object...)}</li>
052 * <li>{@link RenderingContext#addParameterListValues(String, List)}</li>
053 * <li>{@link RenderingContext#setParameterValues(String, Object...)}</li>
054 * <li>{@link RenderingContext#setParameterListValues(String, List)}</li>
055 * </ul>
056 * </p>
057 * <p>
058 * To manage document properties, entity enrichers or properties fetching, use:
059 * <ul>
060 * <li>{@link RenderingContext#getProperties()}</li>
061 * <li>{@link RenderingContext#getEnrichers(String)}</li>
062 * <li>{@link RenderingContext#getFetched(String)}</li>
063 * </ul>
064 * </p>
065 * <p>
066 * To manage infinite loop when calling a marshaller from another marshaller, use:
067 * <ul>
068 * <li>{@link RenderingContext#wrap()} -> {@link WrappedContext#controlDepth()}</li>
069 * </ul>
070 * Example:
071 *
072 * <pre>
073 * // This will control infinite loop in this marshaller
074 * try (Closeable resource = ctx.wrap().controlDepth().open()) {
075 *     // call another marshaller to fetch the desired property here
076 * } catch (MaxDepthReachedException mdre) {
077 *     // do not call the other marshaller
078 * }
079 * </pre>
080 * </p>
081 *
082 * @since 7.2.
083 */
084public interface RenderingContext {
085
086    public static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
087
088    public static final String DEFAULT_URL = "http://fake-url.nuxeo.com/";
089
090    public static final String RESPONSE_HEADER_ENTITY_TYPE_KEY = "ENTITY_TYPE";
091
092    /**
093     * @since 10.2
094     */
095    public static final String REPOSITORY_NAME_REQUEST_HEADER = "X-NXRepository";
096
097    /**
098     * @since 10.2
099     */
100    public static final String REPOSITORY_NAME_REQUEST_PARAMETER = "nxrepository";
101
102    /**
103     * Gets the requested {@link Locale}.
104     *
105     * @since 7.2
106     */
107    public Locale getLocale();
108
109    /**
110     * Gets the current base url.
111     *
112     * @since 7.2
113     */
114    public String getBaseUrl();
115
116    /**
117     * Gets the current {@link CoreSession} or try to create one.
118     *
119     * @param document may be null, if present, this method search for a session in the document.
120     * @return The current {@link CoreSession} if it exists. null otherwise.
121     * @throws MarshallingException if no session could be created or found.
122     * @since 7.2
123     */
124    public SessionWrapper getSession(DocumentModel document) throws MarshallingException;
125
126    /**
127     * Provides a {@link CoreSession} to marshallers.
128     * <p>
129     * For example: a {@link CoreSession} from the request context.
130     * </p>
131     *
132     * @param session The existing {@link CoreSession} which lifecycle is managed outside the marshalling context.
133     * @since 7.2
134     */
135    public void setExistingSession(CoreSession session);
136
137    /**
138     * Get all document properties. This will aggregate all values from parameters "properties", "X-NXproperties" and
139     * "X-NXDocumentProperties". This supports value separated by comma.
140     *
141     * @return All document properties.
142     * @since 7.2
143     */
144    public Set<String> getProperties();
145
146    /**
147     * Get all properties to fetch for a given entity type. This will aggregate all values from parameters
148     * "fetch.entity" and "X-NXfetch.entity". This supports value separated by comma.
149     *
150     * @param entity The type of the entity on which you want to fetch properties.
151     * @return All properties to fetch.
152     * @since 7.2
153     */
154    public Set<String> getFetched(String entity);
155
156    /**
157     * Get all properties to translate for a given entity type. This will aggregate all values from parameters
158     * "translate.entity" and "X-NXtranslate.entity". This supports value separated by comma.
159     *
160     * @param entity The type of the entity on which you want to fetch properties.
161     * @return All properties to fetch.
162     * @since 7.2
163     */
164    public Set<String> getTranslated(String entity);
165
166    /**
167     * Get all enrichers to activate on the given entity type. This will aggregate all values from parameters
168     * "enrichers.entity", "X-NXenrichers.entity" and "X-NXContext-Category". This supports value separated by comma.
169     *
170     * @param entity The type of the entity on which you want to activate enrichers.
171     * @return All enrichers to activate.
172     * @since 7.2
173     */
174    public Set<String> getEnrichers(String entity);
175
176    /**
177     * see {@link WrappedContext}
178     *
179     * @return A new {@link WrappedContext}
180     * @since 7.2
181     */
182    public WrappedContext wrap();
183
184    /**
185     * Get the casted parameter value for a given name. If multiple are available, the first found is returned.
186     *
187     * @param name The parameter name.
188     * @return The first parameter value, null if no parameter are availble.
189     * @since 7.2
190     */
191    public <T> T getParameter(String name);
192
193    /**
194     * see {@link #getParameter(String)}
195     *
196     * @return true is the parameter exists and if it's Boolean.TRUE or "true", false otherwise.
197     */
198    public boolean getBooleanParameter(String name);
199
200    /**
201     * Get the casted parameter values for a given name.
202     *
203     * @param name The parameter name.
204     * @return The parameter values.
205     * @since 7.2
206     */
207    public <T> List<T> getParameters(String name);
208
209    /**
210     * Get all parameter in this context except wrapped parameters.
211     *
212     * @return All parameter's names and their values.
213     * @since 7.2
214     */
215    public Map<String, List<Object>> getAllParameters();
216
217    /**
218     * @see #setParameterListValues(String, List)
219     * @since 7.2
220     */
221    public void setParameterValues(String name, Object... values);
222
223    /**
224     * Push values in the context with a given name. Please note that this method remove any value for the given name.
225     *
226     * @param name The parameter name.
227     * @param values The parameter values.
228     * @since 7.2
229     */
230    public void setParameterListValues(String name, List<Object> values);
231
232    /**
233     * @see #addParameterListValues(String, List)
234     * @since 7.2
235     */
236    public void addParameterValues(String name, Object... values);
237
238    /**
239     * Add values in the context with a given name. Please note that this method keep current values for the given name.
240     *
241     * @param name The parameter name.
242     * @param values The parameter values.
243     * @since 7.2
244     */
245    public void addParameterListValues(String name, List<?> values);
246
247    /**
248     * {@link RenderingContext} builder.
249     * <p>
250     * RenderingContext ctx = CtxBuilder.base("http://mine.nuxeo.com/nuxeo").locale(Locale.ENGLISH).param("name",
251     * "value1", "value2").get();
252     * </p>
253     *
254     * @since 7.2
255     */
256    public static final class CtxBuilder {
257        private CtxBuilder() {
258        }
259
260        public static RenderingContextBuilder builder() {
261            return new RenderingContextBuilder();
262        }
263
264        public static RenderingContextBuilder base(String url) {
265            RenderingContextBuilder builder = new RenderingContextBuilder();
266            return builder.base(url);
267        }
268
269        public static RenderingContextBuilder locale(Locale locale) {
270            RenderingContextBuilder builder = new RenderingContextBuilder();
271            return builder.locale(locale);
272        }
273
274        public static RenderingContextBuilder session(CoreSession session) {
275            RenderingContextBuilder builder = new RenderingContextBuilder();
276            return builder.session(session);
277        }
278
279        public static RenderingContextBuilder param(String name, Object value) {
280            RenderingContextBuilder builder = new RenderingContextBuilder();
281            return builder.param(name, value);
282        }
283
284        public static RenderingContextBuilder paramValues(String name, Object... values) {
285            RenderingContextBuilder builder = new RenderingContextBuilder();
286            return builder.paramValues(name, values);
287        }
288
289        public static RenderingContextBuilder paramList(String name, List<?> values) {
290            RenderingContextBuilder builder = new RenderingContextBuilder();
291            return builder.paramList(name, values);
292        }
293
294        public static RenderingContextBuilder properties(String... schemaName) {
295            RenderingContextBuilder builder = new RenderingContextBuilder();
296            return builder.properties(schemaName);
297        }
298
299        public static RenderingContextBuilder fetch(String entityType, String... propertyName) {
300            RenderingContextBuilder builder = new RenderingContextBuilder();
301            return builder.fetch(entityType, propertyName);
302        }
303
304        public static RenderingContextBuilder fetchInDoc(String... propertyName) {
305            RenderingContextBuilder builder = new RenderingContextBuilder();
306            return builder.fetchInDoc(propertyName);
307        }
308
309        public static RenderingContextBuilder translate(String entityType, String... propertyName) {
310            RenderingContextBuilder builder = new RenderingContextBuilder();
311            return builder.translate(entityType, propertyName);
312        }
313
314        public static RenderingContextBuilder enrich(String entityType, String... enricherName) {
315            RenderingContextBuilder builder = new RenderingContextBuilder();
316            return builder.enrich(entityType, enricherName);
317        }
318
319        public static RenderingContextBuilder enrichDoc(String... enricherName) {
320            RenderingContextBuilder builder = new RenderingContextBuilder();
321            return builder.enrichDoc(enricherName);
322        }
323
324        public static RenderingContextBuilder depth(DepthValues value) {
325            RenderingContextBuilder builder = new RenderingContextBuilder();
326            return builder.depth(value);
327        }
328
329        public static RenderingContext get() {
330            RenderingContextBuilder builder = new RenderingContextBuilder();
331            return builder.get();
332        }
333    }
334
335    /**
336     * Session wrapper that manage the closing of new created session and preserve request scoped or document scoped
337     * session.
338     *
339     * @since 7.2
340     */
341    public class SessionWrapper implements Closeable {
342
343        private CoreSession session;
344
345        private boolean shouldBeClosed;
346
347        public SessionWrapper(CoreSession session, boolean shouldBeClosed) {
348            super();
349            this.session = session;
350            this.shouldBeClosed = shouldBeClosed;
351        }
352
353        public CoreSession getSession() {
354            return session;
355        }
356
357        public boolean shouldBeClosed() {
358            return shouldBeClosed;
359        }
360
361        @Override
362        public void close() throws IOException {
363            if (shouldBeClosed) {
364                ((CloseableCoreSession) session).close();
365            }
366        }
367
368    }
369
370}