/** * This file Copyright (c) 2003-2012 Magnolia International * Ltd. (http://www.magnolia-cms.com). All rights reserved. * * * This file is dual-licensed under both the Magnolia * Network Agreement and the GNU General Public License. * You may elect to use one or the other of these licenses. * * This file is distributed in the hope that it will be * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT. * Redistribution, except as permitted by whichever of the GPL * or MNA you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or * modify this file under the terms of the GNU General * Public License, Version 3, as published by the Free Software * Foundation. You should have received a copy of the GNU * General Public License, Version 3 along with this program; * if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * 2. For the Magnolia Network Agreement (MNA), this file * and the accompanying materials are made available under the * terms of the MNA which accompanies this distribution, and * is available at http://www.magnolia-cms.com/mna.html * * Any modifications to this file must keep this entire header * intact. * */ package info.magnolia.context; import info.magnolia.cms.beans.runtime.MultipartForm; import info.magnolia.cms.core.AggregationState; import info.magnolia.cms.core.HierarchyManager; import info.magnolia.cms.core.search.QueryManager; import info.magnolia.cms.i18n.Messages; import info.magnolia.cms.security.AccessManager; import info.magnolia.cms.security.User; import java.util.Locale; import java.util.Map; import javax.jcr.LoginException; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.security.auth.Subject; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class allows obtaining of the current Request without passing the request around the world. A ThreadLocal variable is used to * manage the request and to make sure it doesn't escape to another processing. * <p> * In a later version this class should not depend on servlets. The core should use the context to get and set * attributes instead of using the request or session object directly. Magnolia could run then in a neutral and * configurable context. * @version $Revision$ ($Author$) */ public class MgnlContext { private static final Logger log = LoggerFactory.getLogger(MgnlContext.class); /** * The thread local variable holding the current context. */ private static ThreadLocal<Context> localContext = new ThreadLocal<Context>(); /** * Do not instantiate this class. The constructor must be public to use discovery */ public MgnlContext() { } /** * A short cut for the current user. * @return the current user */ public static User getUser() { return getInstance().getUser(); } /** * Set the locale for the current context. */ public static void setLocale(Locale locale) { getInstance().setLocale(locale); } /** * Get the context's locale object. * @return the current locale */ public static Locale getLocale() { return getInstance().getLocale(); } public static Messages getMessages() { return getInstance().getMessages(); } public static Messages getMessages(String basename) { return getInstance().getMessages(basename); } public static void login(Subject subject) { ((UserContext)getInstance()).login(subject); } /** * Get hierarchy manager initialized for this user. * * @deprecated since 4.5 - use {@link #getJCRSession(String)} */ @Deprecated public static HierarchyManager getHierarchyManager(String repositoryId) { return getInstance().getHierarchyManager(repositoryId); } /** * Get access manager for a resource, usually a workspace. */ public static AccessManager getAccessManager(String name) { return getInstance().getAccessManager(name); } /** * Get access manager for the specified repository on the specified workspace. * * @deprecated since 4.5 - use {@link #getJCRSession(String)} and acquire the JCR query manager directly from the session. */ @Deprecated public static QueryManager getQueryManager(String workspaceName) { return getInstance().getQueryManager(workspaceName); } /** * Get form object assembled by <code>MultipartRequestFilter</code>. * @return multipart form object * TODO - move to getAggregationState() ? */ public static MultipartForm getPostedForm() { WebContext ctx = getWebContextOrNull(); if (ctx != null) { return ctx.getPostedForm(); } return null; } /** * Get parameter value as string. */ public static String getParameter(String name) { WebContext ctx = getWebContextOrNull(); if (ctx != null) { return ctx.getParameter(name); } return null; } public static String[] getParameterValues(String name) { WebContext ctx = getWebContextOrNull(); if (ctx != null) { return ctx.getParameterValues(name); } return null; } /** * Get parameter value as a Map<String, String>. */ public static Map<String, String> getParameters() { WebContext ctx = getWebContextOrNull(); if (ctx != null) { return ctx.getParameters(); } return null; } /** * @return the context path. */ public static String getContextPath() { WebContext ctx = getWebContextOrNull(); if (ctx != null) { return ctx.getContextPath(); } throw new IllegalStateException("Can only get the context path within a WebContext."); } /** * Returns the AggregationState if we're in a WebContext, throws an * IllegalStateException otherwise. */ public static AggregationState getAggregationState() { final WebContext ctx = getWebContextOrNull(); if (ctx != null) { return ctx.getAggregationState(); } throw new IllegalStateException("Can only get the aggregation state within a WebContext."); } /** * Resets the current aggregation state if we're in a WebContext, throws an IllegalStateException otherwise. */ public static void resetAggregationState() { final WebContext ctx = getWebContextOrNull(); if (ctx != null) { ctx.resetAggregationState(); } else { throw new IllegalStateException("Can only reset the aggregation state within a WebContext."); } } /** * Set attribute value, scope of the attribute is {@link Context#LOCAL_SCOPE}. */ public static void setAttribute(String name, Object value) { getInstance().setAttribute(name, value, Context.LOCAL_SCOPE); } /** * Set attribute value, scope of the attribute is defined. * @param scope , highest level of scope from which this attribute is visible. */ public static void setAttribute(String name, Object value, int scope) { getInstance().setAttribute(name, value, scope); } /** * Get attribute value. */ public static <T> T getAttribute(String name) { return (T) getInstance().getAttribute(name); } /** * Get the attribute from the specified scope. */ public static <T> T getAttribute(String name, int scope) { return (T) getInstance().getAttribute(name, scope); } /** * Check if this attribute exists in the local scope. */ public static boolean hasAttribute(String name){ return getInstance().getAttribute(name, Context.LOCAL_SCOPE) != null; } /** * Check if this attribute exists in the specified scope. */ public static boolean hasAttribute(String name, int scope){ return getInstance().getAttribute(name, scope) != null; } /** * Remove an attribute in the local scope. */ public static void removeAttribute(String name){ getInstance().removeAttribute(name, Context.LOCAL_SCOPE); } /** * Remove an attribute in the specified scope. */ public static void removeAttribute(String name, int scope){ getInstance().removeAttribute(name, scope); } /** * Set context implementation instance. */ public static void setInstance(Context context) { localContext.set(context); } /** * Get the current context of this thread. */ public static Context getInstance() { Context context = localContext.get(); // It should never fall back, We need to fix all false callers instead if (context == null) { final IllegalStateException ise = new IllegalStateException("MgnlContext is not set for this thread"); log.error("MgnlContext is not initialized. This could happen if the request does not go through the Magnolia default filters.", ise); throw ise; } return context; } /** * Throws an IllegalStateException if the current context is not set, or if it is not an instance of WebContext. * @see #getWebContext(String) */ public static WebContext getWebContext() { return getWebContext(null); } /** * Throws an IllegalStateException if the current context is not set, or if it is not an instance of WebContext. * Yes, you can specify the exception message if you want. This is useful if you're calling this from a component * which only supports WebContext and still care enough to actually throw an exception with a meaningful message. * @see #getWebContext() */ public static WebContext getWebContext(String exceptionMessage) { final WebContext wc = getWebContextIfExisting(getInstance()); if (wc == null) { throw new IllegalStateException(exceptionMessage == null ? "The current context is not an instance of WebContext (" + localContext.get() + ")" : exceptionMessage); } return wc; } /** * Returns the current context if it is set and is an instance of WebContext, returns null otherwise. * @return */ public static WebContext getWebContextOrNull() { return hasInstance() ? getWebContextIfExisting(getInstance()) : null; } /** * Used to check if an instance is already set since getInstance() will always return a context. * @return true if an instance was set. */ public static boolean hasInstance() { return localContext.get() != null; } public static boolean isSystemInstance() { return localContext.get() instanceof SystemContext; } /** * Returns true if the current context is set and is an instance of WebContext. (it might be wrapped in a ContextDecorator) */ public static boolean isWebContext() { return hasInstance() && getWebContextIfExisting(getInstance()) != null; } /** * Get Magnolia system context. This context has full permissions over all repositories/ workspaces. * @deprecated since 4.5, use IoC, i.e., declare a dependency on SystemContext in your component. */ @Deprecated public static SystemContext getSystemContext() { return ContextFactory.getInstance().getSystemContext(); } /** * @deprecated since 4.2 - use the Op interface, which can return values, or extend VoidOp. */ @Deprecated public static void doInSystemContext(final SystemContextOperation op) { doInSystemContext(op, false); } /** * Executes the given operation in the system context and sets it back to the original once done * (also if an exception is thrown). Also works if there was no context upon calling. (sets it back * to null in this case) */ public static <T, E extends Throwable> T doInSystemContext(final Op<T, E> op) throws E { return doInSystemContext(op, false); } /** * @deprecated since 4.2 - use the Op interface, which can return values, or extend VoidOp. */ @Deprecated public static void doInSystemContext(final SystemContextOperation op, boolean releaseAfterExecution) { doInSystemContext(new VoidOp() { @Override public void doExec() { op.exec(); } }, releaseAfterExecution); } /** * Executes the given operation in the system context and sets it back to the original once done * (also if an exception is thrown). Also works if there was no context upon calling (sets it back * to null in this case) * @param releaseAfterExecution set to true if the context should be released once the execution is done (e.g. in workflow operations or scheduled jobs). */ public static <T, E extends Throwable> T doInSystemContext(final Op<T, E> op, boolean releaseAfterExecution) throws E { final Context originalCtx = MgnlContext.hasInstance() ? MgnlContext.getInstance() : null; T result; try { MgnlContext.setInstance(MgnlContext.getSystemContext()); result = op.exec(); if (releaseAfterExecution) { MgnlContext.release(); } } finally { MgnlContext.setInstance(originalCtx); } return result; } /** * Deprecated. * @deprecated since 4.2 - use the Op interface, which can return values, or extend VoidOp. * @see info.magnolia.context.MgnlContext.Op * @see info.magnolia.context.MgnlContext.VoidOp */ @Deprecated public static interface SystemContextOperation { void exec(); } /** * A simple execution interface to be used with the doInSystemContext method. * If no return value is necessary, return null (for semantic's sake, declare T as <Void>) * If no checked exception need to be thrown, declare E as <RuntimeException>) * * @see MgnlContext#doInSystemContext(Op op) * @param <T> the return type of this operation * @param <E> an exception this operation can throw */ public static interface Op<T, E extends Throwable> { T exec() throws E; } /** * An Op that does not return values and can only throw RuntimeExceptions. */ public abstract static class VoidOp implements Op<Void, RuntimeException> { @Override public Void exec() { doExec(); return null; } abstract public void doExec(); } /** * Sets this context as a web context. * @param request HttpServletRequest * @param response HttpServletResponse * @param servletContext ServletContext instance * @deprecated since 4.5, use WebContextFactory. */ @Deprecated public static void initAsWebContext(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) { WebContext ctx = ContextFactory.getInstance().createWebContext(request, response, servletContext); setInstance(ctx); } /** * Returns the web context, also if eventually wrapped in a ContextDecorator. * @return WebContext instance or null if no web context is set */ private static WebContext getWebContextIfExisting(Context ctx) { if (ctx instanceof WebContext) { return (WebContext) ctx; } else if (ctx instanceof ContextDecorator) { return getWebContextIfExisting(((ContextDecorator) ctx).getWrappedContext()); } return null; } /** * Releases the current thread (if not a system context) and calls the releaseThread() method of the system context. */ public static void release() { if(hasInstance() && !(getInstance() instanceof SystemContext)){ getInstance().release(); } SystemContext systemContext = getSystemContext(); if(systemContext instanceof ThreadDependentSystemContext){ ((ThreadDependentSystemContext)systemContext).releaseThread(); } } public static void push(HttpServletRequest request, HttpServletResponse response) { if (isWebContext()) { WebContext wc = getWebContext(); wc.push(request,response); } } public static void pop() { if (isWebContext()) { WebContext wc = getWebContext(); wc.pop(); } } /** * Note: this is the way to go, if you no longer want to rely on the Content-API. * * @param workspaceName - repository to get session for * @return a JCR session to the provided repository */ public static Session getJCRSession(String workspaceName) throws LoginException, RepositoryException { return getInstance().getJCRSession(workspaceName); } public static Subject getSubject() { return getInstance().getSubject(); } }