View Javadoc
1   /**
2    * This file Copyright (c) 2003-2014 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.context;
35  
36  import info.magnolia.cms.beans.runtime.MultipartForm;
37  import info.magnolia.cms.core.AggregationState;
38  import info.magnolia.cms.core.HierarchyManager;
39  import info.magnolia.cms.core.search.QueryManager;
40  import info.magnolia.cms.i18n.Messages;
41  import info.magnolia.cms.security.AccessManager;
42  import info.magnolia.cms.security.User;
43  
44  import java.util.Locale;
45  import java.util.Map;
46  
47  import javax.jcr.LoginException;
48  import javax.jcr.RepositoryException;
49  import javax.jcr.Session;
50  import javax.security.auth.Subject;
51  import javax.servlet.ServletContext;
52  import javax.servlet.http.HttpServletRequest;
53  import javax.servlet.http.HttpServletResponse;
54  
55  import org.slf4j.Logger;
56  import org.slf4j.LoggerFactory;
57  
58  
59  /**
60   * This class allows obtaining of the current Request without passing the request around the world. A ThreadLocal variable is used to
61   * manage the request and to make sure it doesn't escape to another processing.
62   * <p>
63   * In a later version this class should not depend on servlets. The core should use the context to get and set
64   * attributes instead of using the request or session object directly. Magnolia could run then in a neutral and
65   * configurable context.
66   * @version $Revision$ ($Author$)
67   */
68  
69  public class MgnlContext {
70      private static final Logger log = LoggerFactory.getLogger(MgnlContext.class);
71  
72      /**
73       * The thread local variable holding the current context.
74       */
75      private static ThreadLocal<Context> localContext = new ThreadLocal<Context>();
76  
77      /**
78       * Do not instantiate this class. The constructor must be public to use discovery
79       */
80      public MgnlContext() {
81      }
82  
83      /**
84       * A short cut for the current user.
85       * @return the current user
86       */
87      public static User getUser() {
88          return getInstance().getUser();
89      }
90  
91      /**
92       * Set the locale for the current context.
93       */
94      public static void setLocale(Locale locale) {
95          getInstance().setLocale(locale);
96      }
97  
98      /**
99       * Get the context's locale object.
100      * @return the current locale
101      */
102     public static Locale getLocale() {
103         return getInstance().getLocale();
104     }
105 
106     public static Messages getMessages() {
107         return getInstance().getMessages();
108     }
109 
110     public static Messages getMessages(String basename) {
111         return getInstance().getMessages(basename);
112     }
113 
114     public static void login(Subject subject) {
115         ((UserContext)getInstance()).login(subject);
116     }
117 
118     /**
119      * Get hierarchy manager initialized for this user.
120      *
121      * @deprecated since 4.5 - use {@link #getJCRSession(String)}
122      */
123     @Deprecated
124     public static HierarchyManager getHierarchyManager(String repositoryId) {
125         return getInstance().getHierarchyManager(repositoryId);
126     }
127 
128     /**
129      * Get access manager for a resource, usually a workspace.
130      */
131     public static AccessManager getAccessManager(String name) {
132         return getInstance().getAccessManager(name);
133     }
134 
135     /**
136      * Get access manager for the specified repository on the specified workspace.
137      *
138      * @deprecated since 4.5 - use {@link #getJCRSession(String)} and acquire the JCR query manager directly from the session.
139      */
140     @Deprecated
141     public static QueryManager getQueryManager(String workspaceName) {
142         return getInstance().getQueryManager(workspaceName);
143     }
144 
145     /**
146      * Get form object assembled by <code>MultipartRequestFilter</code>.
147      * @return multipart form object
148      * TODO - move to getAggregationState() ?
149      */
150     public static MultipartForm getPostedForm() {
151         WebContext ctx = getWebContextOrNull();
152         if (ctx != null) {
153             return ctx.getPostedForm();
154         }
155         return null;
156     }
157 
158     /**
159      * Get parameter value as string.
160      */
161     public static String getParameter(String name) {
162         WebContext ctx = getWebContextOrNull();
163         if (ctx != null) {
164             return ctx.getParameter(name);
165         }
166         return null;
167 
168     }
169 
170     public static String[] getParameterValues(String name) {
171         WebContext ctx = getWebContextOrNull();
172         if (ctx != null) {
173             return ctx.getParameterValues(name);
174         }
175         return null;
176 
177     }
178 
179     /**
180      * Get parameter value as a Map&lt;String, String&gt;.
181      */
182     public static Map<String, String> getParameters() {
183         WebContext ctx = getWebContextOrNull();
184         if (ctx != null) {
185             return ctx.getParameters();
186         }
187         return null;
188     }
189 
190     /**
191      * @return the context path.
192      */
193     public static String getContextPath() {
194         WebContext ctx = getWebContextOrNull();
195         if (ctx != null) {
196             return ctx.getContextPath();
197         }
198         throw new IllegalStateException("Can only get the context path within a WebContext.");
199     }
200 
201     /**
202      * Returns the AggregationState if we're in a WebContext, throws an
203      * IllegalStateException otherwise.
204      */
205     public static AggregationState getAggregationState() {
206         final WebContext ctx = getWebContextOrNull();
207         if (ctx != null) {
208             return ctx.getAggregationState();
209         }
210         throw new IllegalStateException("Can only get the aggregation state within a WebContext.");
211     }
212 
213     /**
214      * Resets the current aggregation state if we're in a WebContext, throws an IllegalStateException otherwise.
215      */
216     public static void resetAggregationState() {
217         final WebContext ctx = getWebContextOrNull();
218         if (ctx != null) {
219             ctx.resetAggregationState();
220         }
221         else {
222             throw new IllegalStateException("Can only reset the aggregation state within a WebContext.");
223         }
224     }
225 
226     /**
227      * Set attribute value, scope of the attribute is {@link Context#LOCAL_SCOPE}.
228      */
229     public static void setAttribute(String name, Object value) {
230         getInstance().setAttribute(name, value, Context.LOCAL_SCOPE);
231     }
232 
233     /**
234      * Set attribute value, scope of the attribute is defined.
235      * @param scope , highest level of scope from which this attribute is visible.
236      */
237     public static void setAttribute(String name, Object value, int scope) {
238         getInstance().setAttribute(name, value, scope);
239     }
240 
241     /**
242      * Get attribute value.
243      */
244     public static <T> T getAttribute(String name) {
245         return (T) getInstance().getAttribute(name);
246     }
247 
248     /**
249      * Get the attribute from the specified scope.
250      */
251     public static <T> T getAttribute(String name, int scope) {
252         return (T) getInstance().getAttribute(name, scope);
253     }
254 
255     /**
256      * Check if this attribute exists in the local scope.
257      */
258     public static boolean hasAttribute(String name){
259         return getInstance().getAttribute(name, Context.LOCAL_SCOPE) != null;
260     }
261 
262     /**
263      * Check if this attribute exists in the specified scope.
264      */
265     public static boolean hasAttribute(String name, int scope){
266         return getInstance().getAttribute(name, scope) != null;
267     }
268 
269     /**
270      * Remove an attribute in the local scope.
271      */
272     public static void removeAttribute(String name){
273         getInstance().removeAttribute(name, Context.LOCAL_SCOPE);
274     }
275 
276     /**
277      * Remove an attribute in the specified scope.
278      */
279     public static void removeAttribute(String name, int scope){
280         getInstance().removeAttribute(name, scope);
281     }
282 
283     /**
284      * Set context implementation instance.
285      */
286     public static void setInstance(Context context) {
287         localContext.set(context);
288     }
289 
290     /**
291      * Get the current context of this thread.
292      */
293     public static Context getInstance() {
294         Context context = localContext.get();
295         // It should never fall back, We need to fix all false callers instead
296         if (context == null) {
297             final IllegalStateException ise = new IllegalStateException("MgnlContext is not set for this thread");
298             log.error("MgnlContext is not initialized. This could happen if the request does not go through the Magnolia default filters.", ise);
299             throw ise;
300         }
301         return context;
302     }
303 
304     /**
305      * Throws an IllegalStateException if the current context is not set, or if it is not an instance of WebContext.
306      * @see #getWebContext(String)
307      */
308     public static WebContext getWebContext() {
309         return getWebContext(null);
310     }
311 
312     /**
313      * Throws an IllegalStateException if the current context is not set, or if it is not an instance of WebContext.
314      * Yes, you can specify the exception message if you want. This is useful if you're calling this from a component
315      * which only supports WebContext and still care enough to actually throw an exception with a meaningful message.
316      * @see #getWebContext()
317      */
318     public static WebContext getWebContext(String exceptionMessage) {
319         final WebContext wc = getWebContextIfExisting(getInstance());
320         if (wc == null) {
321             throw new IllegalStateException(exceptionMessage == null ? "The current context is not an instance of WebContext (" + localContext.get() + ")" : exceptionMessage);
322         }
323         return wc;
324     }
325 
326     /**
327      * Returns the current context if it is set and is an instance of WebContext, returns null otherwise.
328      * @return
329      */
330     public static WebContext getWebContextOrNull() {
331         return hasInstance() ? getWebContextIfExisting(getInstance()) : null;
332     }
333 
334     /**
335      * Used to check if an instance is already set since getInstance() will always return a context.
336      * @return true if an instance was set.
337      */
338     public static boolean hasInstance() {
339         return localContext.get() != null;
340     }
341 
342     public static boolean isSystemInstance() {
343         return localContext.get() instanceof SystemContext;
344     }
345 
346     /**
347      * Returns true if the current context is set and is an instance of WebContext. (it might be wrapped in a ContextDecorator)
348      */
349     public static boolean isWebContext() {
350         return hasInstance() && getWebContextIfExisting(getInstance()) != null;
351     }
352 
353     /**
354      * Get Magnolia system context. This context has full permissions over all repositories/ workspaces.
355      * @deprecated since 4.5, use IoC, i.e., declare a dependency on SystemContext in your component.
356      */
357     @Deprecated
358     public static SystemContext getSystemContext() {
359         return ContextFactory.getInstance().getSystemContext();
360     }
361 
362     /**
363      * @deprecated since 4.2 - use the Op interface, which can return values, or extend VoidOp.
364      */
365     @Deprecated
366     public static void doInSystemContext(final SystemContextOperation op) {
367         doInSystemContext(op, false);
368     }
369 
370     /**
371      * Executes the given operation in the system context and sets it back to the original once done
372      * (also if an exception is thrown). Also works if there was no context upon calling. (sets it back
373      * to null in this case)
374      */
375     public static <T, E extends Throwable> T doInSystemContext(final Op<T, E> op) throws E {
376         return doInSystemContext(op, false);
377     }
378 
379     /**
380      * @deprecated since 4.2 - use the Op interface, which can return values, or extend VoidOp.
381      */
382     @Deprecated
383     public static void doInSystemContext(final SystemContextOperation op, boolean releaseAfterExecution) {
384         doInSystemContext(new VoidOp() {
385             @Override
386             public void doExec() {
387                 op.exec();
388             }
389         }, releaseAfterExecution);
390     }
391 
392     /**
393      * Executes the given operation in the system context and sets it back to the original once done
394      * (also if an exception is thrown). Also works if there was no context upon calling (sets it back
395      * to null in this case)
396      * @param releaseAfterExecution set to true if the context should be released once the execution is done (e.g. in workflow operations or scheduled jobs).
397      */
398     public static <T, E extends Throwable> T doInSystemContext(final Op<T, E> op, boolean releaseAfterExecution) throws E {
399         final Context originalCtx = MgnlContext.hasInstance() ? MgnlContext.getInstance() : null;
400         T result;
401         try {
402             MgnlContext.setInstance(MgnlContext.getSystemContext());
403             result = op.exec();
404             if (releaseAfterExecution) {
405                 MgnlContext.release();
406             }
407         } finally {
408             MgnlContext.setInstance(originalCtx);
409         }
410         return result;
411     }
412 
413     /**
414      * Deprecated.
415      * @deprecated since 4.2 - use the Op interface, which can return values, or extend VoidOp.
416      * @see info.magnolia.context.MgnlContext.Op
417      * @see info.magnolia.context.MgnlContext.VoidOp
418      */
419     @Deprecated
420     public static interface SystemContextOperation {
421         void exec();
422     }
423 
424     /**
425      * A simple execution interface to be used with the doInSystemContext method.
426      * If no return value is necessary, return null (for semantic's sake, declare T as <Void>)
427      * If no checked exception need to be thrown, declare E as <RuntimeException>)
428      *
429      * @see MgnlContext#doInSystemContext(Op op)
430      * @param <T> the return type of this operation
431      * @param <E> an exception this operation can throw
432      */
433     public static interface Op<T, E extends Throwable> {
434         T exec() throws E;
435     }
436 
437     /**
438      * An Op that does not return values and can only throw RuntimeExceptions.
439      */
440     public abstract static class VoidOp implements Op<Void, RuntimeException> {
441         @Override
442         public Void exec() {
443             doExec();
444             return null;
445         }
446 
447         abstract public void doExec();
448     }
449 
450     /**
451      * Sets this context as a web context.
452      * @param request HttpServletRequest
453      * @param response HttpServletResponse
454      * @param servletContext ServletContext instance
455      * @deprecated since 4.5, use WebContextFactory.
456      */
457     @Deprecated
458     public static void initAsWebContext(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) {
459         WebContext ctx = ContextFactory.getInstance().createWebContext(request, response, servletContext);
460         setInstance(ctx);
461     }
462 
463     /**
464      * Returns the web context, also if eventually wrapped in a ContextDecorator.
465      * @return WebContext instance or null if no web context is set
466      */
467     private static WebContext getWebContextIfExisting(Context ctx) {
468         if (ctx instanceof WebContext) {
469             return (WebContext) ctx;
470         }
471         else if (ctx instanceof ContextDecorator) {
472             return getWebContextIfExisting(((ContextDecorator) ctx).getWrappedContext());
473         }
474         return null;
475     }
476 
477     /**
478      * Releases the current thread (if not a system context) and calls the releaseThread() method of the system context.
479      */
480     public static void release() {
481         if(hasInstance() && !(getInstance() instanceof SystemContext)){
482             getInstance().release();
483         }
484         SystemContext systemContext = getSystemContext();
485         if(systemContext instanceof ThreadDependentSystemContext){
486             ((ThreadDependentSystemContext)systemContext).releaseThread();
487         }
488     }
489 
490     public static void push(HttpServletRequest request, HttpServletResponse response) {
491         if (isWebContext()) {
492             WebContext wc = getWebContext();
493             wc.push(request,response);
494         }
495     }
496 
497     public static void pop() {
498         if (isWebContext()) {
499             WebContext wc = getWebContext();
500             wc.pop();
501         }
502     }
503 
504     /**
505      * Note: this is the way to go, if you no longer want to rely on the Content-API.
506      *
507      * @param workspaceName - repository to get session for
508      * @return a JCR session to the provided repository
509      */
510     public static Session getJCRSession(String workspaceName) throws LoginException, RepositoryException {
511         return getInstance().getJCRSession(workspaceName);
512     }
513 
514     public static Subject getSubject() {
515         return getInstance().getSubject();
516     }
517 }