/*
* Copyright 2012 The Solmix Project
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.gnu.org/licenses/
* or see the FSF site: http://www.fsf.org.
*/
package org.solmix.fmk;
import java.util.Locale;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.solmix.api.call.DSCall;
import org.solmix.api.context.WebContext;
import org.solmix.api.exception.SlxException;
import org.solmix.ds.context.Context;
import org.solmix.ds.context.Context.Scope;
import org.solmix.ds.context.support.ContextDecorator;
import org.solmix.fmk.call.Transaction;
import org.solmix.runtime.SystemContext;
import org.solmix.runtime.SystemContextFactory;
/**
* This class allows obtaining of the current Request without passing the request around the world.
* <p>
* A ThreadLocal variable is used to manage the request and to make sure it doesn't escape to another processing.
* <p>
* This class is not depend on servlets. the framework should used the context to get and set attributes instead of
* using the request or session object directly. Solmix can run with POJO,OSGI,Spring-servlets ,the must has a neutral
* and configurable context.
*
* @author solmix.f@gmail.com
* @version 110081 2013-3-26
* @since 0.0.2
*/
public final class SlxContext
{
private static final Logger log = LoggerFactory.getLogger(SlxContext.class);
/**
* The thread local variable holding the current context.
*/
private static InheritableThreadLocal<Context> localContext = new InheritableThreadLocal<Context>();
/**
* Do not instantiate this class. The constructor must be public to use discovery
*/
public SlxContext()
{
}
/**
* Get the default systemContext,code example:
* <code>
* final SystemContext sc= SlxContext.getSystemContext();
* .....
* .....
* </code>
* @return the systemContext
*/
public static SystemContext getSystemContext() {
return SystemContextFactory.getDefaultSystemContext();
}
/**
* Get the systemContext of the thread,code example:
* <code>
* final SystemContext sc= SlxContext.getThreadSystemContext();
* .....
* .....
* </code>
* @return
*/
public static SystemContext getThreadSystemContext() {
return SystemContextFactory.getThreadDefaultSystemContext();
}
/**
* At most condition must reset the SystemContext after you used completely.
* <p>
* <code>
* Context original =SlxContext.getContext();
* .....
* SlxContext.setContext(otherContext);
* .....
* reset context.
* SlxContext.setContext(originalCtx);
*
* </code>
*
* @param systemContext the systemContext to set
*/
public static void setSystemContext(SystemContext systemContext) {
SystemContextFactory.setDefaultSystemContext(systemContext);
}
/**
* set the thread dependency system context.
* @param systemContext
*/
public static void setThreadSystemContext(SystemContext systemContext){
SystemContextFactory.setThreadDefaultSystemContext(systemContext);
}
/**
* Get the current context of this thread.
*/
public static Context getContext() {
Context context = localContext.get();
if (context == null) {
final IllegalStateException ise = new IllegalStateException("Context is not set for this thread");
log.error("Context is not initialized. This could happen if the request does not go through the default context filters.", ise);
throw ise;
}
return context;
}
public static void removeContext() {
localContext.remove();
}
/**
* Set the context for locale thread.
*
* @param context
*/
public static void setContext(Context context) {
localContext.set(context);
}
public static boolean hasContext() {
return localContext.get() != null;
}
/**
* Set the locale for the current context.
*/
public static void setLocale(Locale locale) {
getWebContext().setLocale(locale);
}
/**
* Get Locale in this thread.
*
* @return
*/
public static Locale getLocale() {
return getWebContext().getLocale();
}
/**
* Get parameter in web context.
*
* @return
*/
public static Map<String, String> getParameters() {
WebContext ctx = getWebContext();
if (ctx != null) {
return ctx.getParameters();
}
return null;
}
/**
* Get parameter value as string.
*/
public static String getParameter(String name) {
WebContext ctx = getWebContext();
if (ctx != null) {
return ctx.getParameter(name);
}
return null;
}
/**
* @return
*/
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(getContext());
if (wc == null) {
throw new IllegalStateException(exceptionMessage == null ? "The current context is not an instance of WebContext (" + localContext.get()
+ ")" : exceptionMessage);
}
return wc;
}
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;
}
public static boolean isSystemContext() {
return hasContext() ? getContext() instanceof SystemContext : false;
}
public static boolean isWebContext() {
return hasContext() ? getContext() instanceof WebContext : false;
}
/**
* Releases the current thread (if not a system context) and calls the releaseThread() method of the system context.
*/
public static void release() {
if (hasContext() && !(getContext() instanceof SystemContext)) {
getContext().close();
}
}
/**
* @param attributeLoginerror
* @param loginResult
*/
public static void setAttribute(String name, Object value) {
getContext().setAttribute(name, value, Scope.LOCAL);
}
/**
* @param attributeLoginerror
* @return
*/
public static Object getAttribute(String name) {
return getContext().getAttribute(name);
}
/**
* 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);
}
/**
* 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 = SlxContext.hasContext() ? SlxContext.getContext() : null;
T result;
try {
SlxContext.setContext(SlxContext.getSystemContext());
result = op.exe();
if (releaseAfterExecution) {
SlxContext.release();
}
} finally {
SlxContext.setContext(originalCtx);
}
return result;
}
/**
* Executes the given operation in the web context ,if this thread is not has a
* {@link org.solmix.api.context.WebContext} in it, return null.
*
* @param op
* @param releaseAfterExecution
* @return
* @throws E
*/
public static <T, E extends Throwable> T doInWebContext(final Op<T, E> op) throws E {
if (SlxContext.hasContext() && SlxContext.isWebContext()) {
return op.exe();
} else {
log.warn("This is not a WebContext");
return null;
}
}
public static Transaction getTransaction() throws SlxException {
return new Transaction(getThreadSystemContext());
}
/**
* @param rpc used exited rpc to complete transaction.
* @return
*/
public static Transaction getTransaction(DSCall rpc) {
return new Transaction(rpc,getThreadSystemContext());
}
/**
* A simple execution interface to be used with the doInSystemContext and doInWebContext 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 SlxContext#doInSystemContext(Op)
* @see SlxContext#doInSystemContext(Op, boolean)
* @see SlxContext#doInWebContext(Op)
*
* @author solmix
*
* @param <T>
* @param <E>
*/
public static interface Op<T, E extends Throwable>
{
T exe() 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 exe() {
doExe();
return null;
}
abstract public void doExe();
}
}