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