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 *
081 * </p>
082 *
083 * @since 7.2.
084 */
085public interface RenderingContext {
086
087    public static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
088
089    public static final String DEFAULT_URL = "http://fake-url.nuxeo.com/";
090
091    public static final String RESPONSE_HEADER_ENTITY_TYPE_KEY = "ENTITY_TYPE";
092
093    /**
094     * Gets the requested {@link Locale}.
095     *
096     * @since 7.2
097     */
098    public Locale getLocale();
099
100    /**
101     * Gets the current base url.
102     *
103     * @since 7.2
104     */
105    public String getBaseUrl();
106
107    /**
108     * Gets the current {@link CoreSession} or try to create one.
109     *
110     * @param document may be null, if present, this method search for a session in the document.
111     * @return The current {@link CoreSession} if it exists. null otherwise.
112     * @throws MarshallingException if no session could be created or found.
113     * @since 7.2
114     */
115    public SessionWrapper getSession(DocumentModel document) throws MarshallingException;
116
117    /**
118     * Provides a {@link CoreSession} to marshallers.
119     * <p>
120     * For example: a {@link CoreSession} from the request context.
121     * </p>
122     *
123     * @param session The existing {@link CoreSession} which lifecycle is managed outside the marshalling context.
124     * @since 7.2
125     */
126    public void setExistingSession(CoreSession session);
127
128    /**
129     * Get all document properties. This will aggregate all values from parameters "properties", "X-NXproperties" and
130     * "X-NXDocumentProperties". This supports value separated by comma.
131     *
132     * @return All document properties.
133     * @since 7.2
134     */
135    public Set<String> getProperties();
136
137    /**
138     * Get all properties to fetch for a given entity type. This will aggregate all values from parameters
139     * "fetch.entity" and "X-NXfetch.entity". This supports value separated by comma.
140     *
141     * @param entity The type of the entity on which you want to fetch properties.
142     * @return All properties to fetch.
143     * @since 7.2
144     */
145    public Set<String> getFetched(String entity);
146
147    /**
148     * Get all properties to translate for a given entity type. This will aggregate all values from parameters
149     * "translate.entity" and "X-NXtranslate.entity". This supports value separated by comma.
150     *
151     * @param entity The type of the entity on which you want to fetch properties.
152     * @return All properties to fetch.
153     * @since 7.2
154     */
155    public Set<String> getTranslated(String entity);
156
157    /**
158     * Get all enrichers to activate on the given entity type. This will aggregate all values from parameters
159     * "enrichers.entity", "X-NXenrichers.entity" and "X-NXContext-Category". This supports value separated by comma.
160     *
161     * @param entity The type of the entity on which you want to activate enrichers.
162     * @return All enrichers to activate.
163     * @since 7.2
164     */
165    public Set<String> getEnrichers(String entity);
166
167    /**
168     * see {@link WrappedContext}
169     *
170     * @return A new {@link WrappedContext}
171     * @since 7.2
172     */
173    public WrappedContext wrap();
174
175    /**
176     * Get the casted parameter value for a given name. If multiple are available, the first found is returned.
177     *
178     * @param name The parameter name.
179     * @return The first parameter value, null if no parameter are availble.
180     * @since 7.2
181     */
182    public <T> T getParameter(String name);
183
184    /**
185     * see {@link #getParameter(String)}
186     *
187     * @return true is the parameter exists and if it's Boolean.TRUE or "true", false otherwise.
188     */
189    public boolean getBooleanParameter(String name);
190
191    /**
192     * Get the casted parameter values for a given name.
193     *
194     * @param name The parameter name.
195     * @return The parameter values.
196     * @since 7.2
197     */
198    public <T> List<T> getParameters(String name);
199
200    /**
201     * Get all parameter in this context except wrapped parameters.
202     *
203     * @return All parameter's names and their values.
204     * @since 7.2
205     */
206    public Map<String, List<Object>> getAllParameters();
207
208    /**
209     * @see #setParameterListValues(String, List)
210     * @since 7.2
211     */
212    public void setParameterValues(String name, Object... values);
213
214    /**
215     * Push values in the context with a given name. Please note that this method remove any value for the given name.
216     *
217     * @param name The parameter name.
218     * @param values The parameter values.
219     * @since 7.2
220     */
221    public void setParameterListValues(String name, List<Object> values);
222
223    /**
224     * @see #addParameterListValues(String, List)
225     * @since 7.2
226     */
227    public void addParameterValues(String name, Object... values);
228
229    /**
230     * Add values in the context with a given name. Please note that this method keep current values for the given name.
231     *
232     * @param name The parameter name.
233     * @param values The parameter values.
234     * @since 7.2
235     */
236    public void addParameterListValues(String name, List<?> values);
237
238    /**
239     * {@link RenderingContext} builder.
240     * <p>
241     * RenderingContext ctx = CtxBuilder.base("http://mine.nuxeo.com/nuxeo").locale(Locale.ENGLISH).param("name",
242     * "value1", "value2").get();
243     * </p>
244     *
245     * @since 7.2
246     */
247    public static final class CtxBuilder {
248        private CtxBuilder() {
249        }
250
251        public static RenderingContextBuilder builder() {
252            return new RenderingContextBuilder();
253        }
254
255        public static RenderingContextBuilder base(String url) {
256            RenderingContextBuilder builder = new RenderingContextBuilder();
257            return builder.base(url);
258        }
259
260        public static RenderingContextBuilder locale(Locale locale) {
261            RenderingContextBuilder builder = new RenderingContextBuilder();
262            return builder.locale(locale);
263        }
264
265        public static RenderingContextBuilder session(CoreSession session) {
266            RenderingContextBuilder builder = new RenderingContextBuilder();
267            return builder.session(session);
268        }
269
270        public static RenderingContextBuilder param(String name, Object value) {
271            RenderingContextBuilder builder = new RenderingContextBuilder();
272            return builder.param(name, value);
273        }
274
275        public static RenderingContextBuilder paramValues(String name, Object... values) {
276            RenderingContextBuilder builder = new RenderingContextBuilder();
277            return builder.paramValues(name, values);
278        }
279
280        public static RenderingContextBuilder paramList(String name, List<?> values) {
281            RenderingContextBuilder builder = new RenderingContextBuilder();
282            return builder.paramList(name, values);
283        }
284
285        public static RenderingContextBuilder properties(String... schemaName) {
286            RenderingContextBuilder builder = new RenderingContextBuilder();
287            return builder.properties(schemaName);
288        }
289
290        public static RenderingContextBuilder fetch(String entityType, String... propertyName) {
291            RenderingContextBuilder builder = new RenderingContextBuilder();
292            return builder.fetch(entityType, propertyName);
293        }
294
295        public static RenderingContextBuilder fetchInDoc(String... propertyName) {
296            RenderingContextBuilder builder = new RenderingContextBuilder();
297            return builder.fetchInDoc(propertyName);
298        }
299
300        public static RenderingContextBuilder translate(String entityType, String... propertyName) {
301            RenderingContextBuilder builder = new RenderingContextBuilder();
302            return builder.translate(entityType, propertyName);
303        }
304
305        public static RenderingContextBuilder enrich(String entityType, String... enricherName) {
306            RenderingContextBuilder builder = new RenderingContextBuilder();
307            return builder.enrich(entityType, enricherName);
308        }
309
310        public static RenderingContextBuilder enrichDoc(String... enricherName) {
311            RenderingContextBuilder builder = new RenderingContextBuilder();
312            return builder.enrichDoc(enricherName);
313        }
314
315        public static RenderingContextBuilder depth(DepthValues value) {
316            RenderingContextBuilder builder = new RenderingContextBuilder();
317            return builder.depth(value);
318        }
319
320        public static RenderingContext get() {
321            RenderingContextBuilder builder = new RenderingContextBuilder();
322            return builder.get();
323        }
324    }
325
326    /**
327     * Session wrapper that manage the closing of new created session and preserve request scoped or document scoped
328     * session.
329     *
330     * @since 7.2
331     */
332    public class SessionWrapper implements Closeable {
333
334        private CoreSession session;
335
336        private boolean shouldBeClosed;
337
338        public SessionWrapper(CoreSession session, boolean shouldBeClosed) {
339            super();
340            this.session = session;
341            this.shouldBeClosed = shouldBeClosed;
342        }
343
344        public CoreSession getSession() {
345            return session;
346        }
347
348        public boolean shouldBeClosed() {
349            return shouldBeClosed;
350        }
351
352        @Override
353        public void close() throws IOException {
354            if (shouldBeClosed) {
355                ((CloseableCoreSession) session).close();
356            }
357        }
358
359    }
360
361}