/* * JBoss, Home of Professional Open Source. * Copyright 2010, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * 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 should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.seam.conversation.spi; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; import java.util.logging.Logger; import javax.enterprise.inject.Alternative; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.Annotated; import javax.enterprise.inject.spi.InjectionPoint; import javax.servlet.http.HttpServletRequest; /** * Create SeamConversationContext based on underlying CDI implementation. * * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a> */ public class SeamConversationContextFactory { private static Logger log = Logger.getLogger(SeamConversationContextFactory.class.getName()); private static boolean disableNoopInstance; private static SeamConversationContext NOOP_INSTANCE = new NoopSeamConversationContext(); private static Map<String, SeamConversationContext> contexts; static { disableNoopInstance = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { return Boolean.getBoolean("seam.conversation.disable.noop"); } }); } /** * Produce matching Seam conversation context. * * @param ip current injection point * @return new Seam conversation context instance */ @SuppressWarnings({"unchecked"}) @Produces public static SeamConversationContext produce(InjectionPoint ip) { Annotated annotated = ip.getAnnotated(); Class<?> storeType = null; if (annotated != null) { Type baseType = annotated.getBaseType(); if (baseType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) baseType; storeType = (Class<?>) pt.getActualTypeArguments()[0]; } } return getContext(storeType); } /** * Get the current Seam conversation context instance. * If null is passed as store type, * we try to use CDI impl's default HTTP based SeamConversationContext. * * @param storeType the store type * @return get current conversation context instance */ @SuppressWarnings({"unchecked"}) public static synchronized <T> SeamConversationContext<T> getContext(Class<T> storeType) { if (contexts == null) contexts = new HashMap<String, SeamConversationContext>(); if (storeType == null) storeType = (Class<T>) HttpServletRequest.class; String type = storeType.getName(); SeamConversationContext scc = contexts.get(type); if (scc == null) { scc = create(storeType); contexts.put(type, scc); } return scc; } /** * Create new SeamConversationContext instance, based on underlying CDI impl. * * @param storeType the store type * @return new Seam conversation context */ @SuppressWarnings({"unchecked"}) private static <T> SeamConversationContext<T> create(Class<T> storeType) { ServiceLoader<SeamConversationContext> loader = ServiceLoader.load(SeamConversationContext.class); for (SeamConversationContext scc : loader) { if (match(scc, storeType)) return scc; } if (disableNoopInstance == false) { log.warning("No matching SeamConversationContext for store type " + storeType + ", using NOOP instance!"); return NOOP_INSTANCE; } else throw new IllegalArgumentException("No matching CDI environment available: " + storeType); } /** * Match current Seam conversation context instance with store type. * * @param scc current Seam conversation context * @param storeType the store type * @return true if scc can be used together with store type, false otherwise */ private static boolean match(SeamConversationContext scc, final Class<?> storeType) { // TODO -- any better way of doing this? try { Class<?> current = scc.getClass(); while (current != Object.class) { final Class<?> clazz = current; Method doAssociate = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() { public Method run() throws Exception { try { // TODo -- impl detail return clazz.getDeclaredMethod("doAssociate", storeType); } catch (NoSuchMethodException nsme) { return null; } } }); if (doAssociate != null) return true; current = current.getSuperclass(); } } catch (Throwable ignored) { } return false; } /** * Do we allow noop Seam conversation context instance. * If disabled, an exception will be thrown for no match. * * @param disableNoopInstance the noop flag */ public static void setDisableNoopInstance(boolean disableNoopInstance) { SeamConversationContextFactory.disableNoopInstance = disableNoopInstance; } @Alternative private static class NoopSeamConversationContext implements SeamConversationContext { public SeamConversationContext associate(Object storage) { return this; } public SeamConversationContext activate(String conversationId) { return this; } public SeamConversationContext invalidate() { return this; } public SeamConversationContext deactivate() { return this; } public SeamConversationContext dissociate(Object storage) { return this; } } }