/* * 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.runtime; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Iterator; import java.util.Map; import java.util.WeakHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.solmix.commons.util.ClassLoaderUtils; /** * * @author solmix.f@gmail.com * @version $Id$ 2013-11-2 */ public abstract class ContainerFactory { public static final String DEFAULT_FACTORY = "org.solmix.runtime.support.ContainerFactoryImpl"; public static final String FACTORY_PROPERTY_NAME = "org.solmix.runtime.ContainerFactory"; protected static Container defaultContainer; protected static final Logger log = LoggerFactory.getLogger(ContainerFactory.class); protected static ThreadLocal<ContainerHolder> threadContainer = new ThreadLocal<ContainerHolder>(); protected static Map<Thread, ContainerHolder> threadContainers = new WeakHashMap<Thread, ContainerHolder>(8); /** * Returns the default system container, creating it if necessary. * * @return the default system container. */ public static synchronized Container getDefaultContainer() { return getDefaultContainer(true); } /** * Return the default system container. * * @param b * @return */ public static synchronized Container getDefaultContainer(boolean createIfNeeded) { if (defaultContainer == null && createIfNeeded) { defaultContainer = newInstance().createContainer(); } if (defaultContainer == null) { // never set up. return null; } else { return defaultContainer; } } /** * Sets the default bus if a default container is not already set. * * @param container the default container. * @return true if the Container was not set and is now set */ public static synchronized boolean possiblySetDefaultContainer(Container container) { ContainerHolder h = getThreadContainerHolder(false); if (h.container == null) { h.container=container; } if (defaultContainer == null) { defaultContainer = container; return true; } return false; } /** * Create a new ContainerFactory the class of {@link ContainerFactory} is determined by looking for the * system property:org.solmix.container.system.fatory or by searching the classpath for: * META-INF/services/org.solmix.runtime.ContainerFactory * * @return */ public static ContainerFactory newInstance() { return newInstance(null); } public static ContainerFactory newInstance(String className) { ContainerFactory instance = null; if (className == null) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); className = getFactoryClass(loader); if (className == null && loader != ContainerFactory.class.getClassLoader()) { className = getFactoryClass(ContainerFactory.class.getClassLoader()); } } if (className == null) { className = ContainerFactory.DEFAULT_FACTORY; } Class<? extends ContainerFactory> factoryClass; try { factoryClass = ClassLoaderUtils.loadClass(className, ContainerFactory.class).asSubclass(ContainerFactory.class); instance = factoryClass.newInstance(); } catch (Exception ex) { log.error("Container instance exception", ex); throw new RuntimeException(ex); } return instance; } /** * @param loader * @return */ public static String getFactoryClass(ClassLoader classLoader) { String factoryClass = null; // next check system properties factoryClass = System.getProperty(FACTORY_PROPERTY_NAME); if (factoryClass != null && "".equals(factoryClass.trim())) { return factoryClass; } try { // next, check for the services stuff in the jar file String serviceId = "META-INF/services/" + ContainerFactory.FACTORY_PROPERTY_NAME; InputStream is = null; if (classLoader == null) { classLoader = Thread.currentThread().getContextClassLoader(); } if (classLoader == null) { is = ClassLoader.getSystemResourceAsStream(serviceId); } else { is = classLoader.getResourceAsStream(serviceId); } if (is != null) { BufferedReader rd = null; try { rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); factoryClass = rd.readLine(); // busFactoryCondition = rd.readLine(); } finally { if (rd != null) { rd.close(); } } } if (factoryClass != null &&! "".equals(factoryClass.trim())) { // XXX In OSGI maybe need import another class,and those class can configured in // META-INF/services/xxx,this version we just used dynamic-import:* return factoryClass; } else { factoryClass = DEFAULT_FACTORY; } } catch (Exception ex) { log.error("Failed found system container factory class", ex); } return factoryClass; } public static synchronized void setDefaultContainer(Container container) { defaultContainer = container; ContainerHolder h = getThreadContainerHolder(false); h.container = container; if (container == null) { h.stale = true; threadContainer.remove(); } } public static void setThreadDefaultContainer(Container container) { if (container == null) { ContainerHolder h = threadContainer.get(); if (h == null) { Thread cur = Thread.currentThread(); synchronized (threadContainers) { h = threadContainers.get(cur); } } if (h != null) { h.container = null; h.stale = true; threadContainer.remove(); } } else { ContainerHolder h = getThreadContainerHolder(true); h.container = container; } } public static Container getThreadDefaultContainer() { return getThreadDefaultContainer(true); } public static Container getThreadDefaultContainer(boolean createIfNeeded) { if (createIfNeeded) { ContainerHolder h = getThreadContainerHolder(false); if (h.container == null) { h.container = createThreadContainer(); } return h.container; } ContainerHolder h = threadContainer.get(); if (h == null || h.stale) { Thread cur = Thread.currentThread(); synchronized (threadContainers) { h = threadContainers.get(cur); } } return h == null || h.stale ? null : h.container; } private static synchronized Container createThreadContainer() { ContainerHolder h = getThreadContainerHolder(false); if (h.container == null) { h.container = getDefaultContainer(true); } return h.container; } private static ContainerHolder getThreadContainerHolder(boolean set) { ContainerHolder h = threadContainer.get(); if (h == null || h.stale) { Thread cur = Thread.currentThread(); synchronized (threadContainers) { h = threadContainers.get(cur); } if (h == null || h.stale) { h = new ContainerHolder(); synchronized (threadContainers) { threadContainers.put(cur, h); } } if (set) { threadContainer.set(h); } } return h; } /** * Sets the default Container for the thread. * * @param container the new thread default container * @return the old thread default system container or null */ public static Container getAndSetThreadDefaultContainer(Container container) { if (container == null) { ContainerHolder h = threadContainer.get(); if (h == null) { Thread cur = Thread.currentThread(); synchronized (threadContainers) { h = threadContainers.get(cur); } } if (h != null) { Container orig = h.container; h.container = null; h.stale = true; threadContainer.remove(); return orig; } return null; } ContainerHolder b = getThreadContainerHolder(true); Container old = b.container; b.container = container; return old; } /** * Removes a Container from being a thread default container for any thread. * <p> * This is typically done when a system container has ended its lifecycle (i.e.: a call to * {@link Container#shutdown(boolean)} was invoked) and it wants to remove any reference to itself for any * thread. * * @param bus the bus to remove */ public static void clearDefaultContainerForAnyThread(final Container container) { synchronized (threadContainers) { for (final Iterator<ContainerHolder> iterator = threadContainers.values().iterator(); iterator.hasNext();) { ContainerHolder item = iterator.next(); if (container == null || item == null || item.container == null || item.stale || container.equals(item.container)) { if (item != null) { item.container = null; // mark as stale so if a thread asks again, it will create a new one item.stale = true; } // This will remove the BusHolder from the only place that should // strongly reference it iterator.remove(); } } } } protected void initializeContainer(Container container) { } public abstract Container createContainer(); static class ContainerHolder { volatile boolean stale; Container container; } }