| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.cxf; |
| |
| import java.io.BufferedReader; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import org.apache.cxf.common.classloader.ClassLoaderUtils; |
| import org.apache.cxf.common.logging.LogUtils; |
| import org.apache.cxf.common.util.SystemPropertyAction; |
| |
| /** |
| * Factory to create CXF Bus objects. |
| * <p>CXF includes a large number of components that provide services, such |
| * as WSDL parsing, and message processing. To avoid creating these objects over and over, and to |
| * allow them to be shared easily, they are associated with a data structure called a bus. |
| * </p> |
| * <p> |
| * You don't ever have to explicitly create or manipulate bus objects. If you simply use the CXF |
| * or JAX-WS APIs to create clients or servers, CXF will create a default bus for you. You can create a bus |
| * explicitly if you need to customize components on the bus or maintain several independent buses |
| * with independent configurations. |
| * </p> |
| * <p> |
| * This class maintains the default bus for the entire process and a set of thread-default buses. All CXF |
| * components that reference the bus, which is to say all CXF components, will obtain a default bus from this |
| * class if you do not set a specific bus. |
| * </p> |
| * <p> |
| * If you create a bus when there is no default bus in effect, that bus will become the default bus. |
| * </p> |
| * <p> |
| * This class holds a reference to the global default bus and a reference to each thread default |
| * bus. The thread references are weak with respect to the threads, but otherwise ordinary. |
| * Thus, so long as the thread remains alive |
| * there will be a strong reference to the bus, and it will not get garbage-collected. |
| * If you want to recover memory used CXF, you can set |
| * the default and per-thread default bus to null, explicitly. |
| * </p> |
| */ |
| public abstract class BusFactory { |
| |
| public static final String BUS_FACTORY_PROPERTY_NAME = "org.apache.cxf.bus.factory"; |
| public static final String DEFAULT_BUS_FACTORY = "org.apache.cxf.bus.CXFBusFactory"; |
| |
| protected static Bus defaultBus; |
| |
| static class BusHolder { |
| Bus bus; |
| volatile boolean stale; |
| } |
| |
| protected static final Map<Thread, BusHolder> THREAD_BUSSES = new WeakHashMap<Thread, BusHolder>(); |
| protected static final ThreadLocal<BusHolder> THREAD_BUS = new ThreadLocal<BusHolder>(); |
| |
| private static final Logger LOG = LogUtils.getL7dLogger(BusFactory.class); |
| |
| /** |
| * Creates a new bus. While concrete <code>BusFactory</code> may offer differently parameterized methods |
| * for creating a bus, all factories support this no-arg factory method. |
| * |
| * @return the newly created bus. |
| */ |
| public abstract Bus createBus(); |
| |
| /** |
| * Returns the default bus, creating it if necessary. |
| * |
| * @return the default bus. |
| */ |
| public static synchronized Bus getDefaultBus() { |
| return getDefaultBus(true); |
| } |
| |
| /** |
| * Returns the default bus |
| * |
| * @param createIfNeeded Set to true to create a default bus if one doesn't exist |
| * @return the default bus. |
| */ |
| public static synchronized Bus getDefaultBus(boolean createIfNeeded) { |
| if (defaultBus == null && createIfNeeded) { |
| defaultBus = newInstance().createBus(); |
| } |
| if (defaultBus == null) { |
| // never set up. |
| return null; |
| } else { |
| return defaultBus; |
| } |
| } |
| |
| private static BusHolder getThreadBusHolder(boolean set) { |
| BusHolder h = THREAD_BUS.get(); |
| if (h == null || h.stale) { |
| Thread cur = Thread.currentThread(); |
| synchronized (THREAD_BUSSES) { |
| h = THREAD_BUSSES.get(cur); |
| } |
| if (h == null || h.stale) { |
| h = new BusHolder(); |
| |
| synchronized (THREAD_BUSSES) { |
| THREAD_BUSSES.put(cur, h); |
| } |
| } |
| if (set) { |
| THREAD_BUS.set(h); |
| } |
| } |
| return h; |
| } |
| |
| |
| /** |
| * Sets the default bus. |
| * |
| * @param bus the default bus. |
| */ |
| public static synchronized void setDefaultBus(Bus bus) { |
| defaultBus = bus; |
| BusHolder b = getThreadBusHolder(false); |
| b.bus = bus; |
| if (bus == null) { |
| b.stale = true; |
| THREAD_BUS.remove(); |
| } |
| } |
| |
| /** |
| * Sets the default bus for the thread. |
| * |
| * @param bus the default bus. |
| */ |
| public static void setThreadDefaultBus(Bus bus) { |
| if (bus == null) { |
| BusHolder h = THREAD_BUS.get(); |
| if (h == null) { |
| Thread cur = Thread.currentThread(); |
| synchronized (THREAD_BUSSES) { |
| h = THREAD_BUSSES.get(cur); |
| } |
| } |
| if (h != null) { |
| h.bus = null; |
| h.stale = true; |
| THREAD_BUS.remove(); |
| } |
| } else { |
| BusHolder b = getThreadBusHolder(true); |
| b.bus = bus; |
| } |
| } |
| |
| /** |
| * Sets the default bus for the thread. |
| * |
| * @param bus the new thread default bus. |
| * @return the old thread default bus or null |
| */ |
| public static Bus getAndSetThreadDefaultBus(Bus bus) { |
| if (bus == null) { |
| BusHolder b = THREAD_BUS.get(); |
| if (b == null) { |
| Thread cur = Thread.currentThread(); |
| synchronized (THREAD_BUSSES) { |
| b = THREAD_BUSSES.get(cur); |
| } |
| } |
| if (b != null) { |
| Bus orig = b.bus; |
| b.bus = null; |
| b.stale = true; |
| THREAD_BUS.remove(); |
| return orig; |
| } |
| return null; |
| } |
| BusHolder b = getThreadBusHolder(true); |
| Bus old = b.bus; |
| b.bus = bus; |
| return old; |
| } |
| |
| /** |
| * Gets the default bus for the thread. |
| * |
| * @return the default bus. |
| */ |
| public static Bus getThreadDefaultBus() { |
| return getThreadDefaultBus(true); |
| } |
| |
| /** |
| * Gets the default bus for the thread, creating if needed |
| * |
| * @param createIfNeeded Set to true to create a default bus if one doesn't exist |
| * @return the default bus. |
| */ |
| public static Bus getThreadDefaultBus(boolean createIfNeeded) { |
| if (createIfNeeded) { |
| BusHolder b = getThreadBusHolder(false); |
| if (b.bus == null) { |
| b.bus = createThreadBus(); |
| } |
| return b.bus; |
| } |
| BusHolder h = THREAD_BUS.get(); |
| if (h == null || h.stale) { |
| Thread cur = Thread.currentThread(); |
| synchronized (THREAD_BUSSES) { |
| h = THREAD_BUSSES.get(cur); |
| } |
| } |
| return h == null || h.stale ? null : h.bus; |
| } |
| private static synchronized Bus createThreadBus() { |
| BusHolder b = getThreadBusHolder(false); |
| if (b.bus == null) { |
| b.bus = getDefaultBus(true); |
| } |
| return b.bus; |
| } |
| /** |
| * Removes a bus from being a thread default bus for any thread. |
| * <p> |
| * This is typically done when a bus has ended its lifecycle (i.e.: a call to |
| * {@link Bus#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 clearDefaultBusForAnyThread(final Bus bus) { |
| synchronized (THREAD_BUSSES) { |
| for (final Iterator<BusHolder> iterator = THREAD_BUSSES.values().iterator(); |
| iterator.hasNext();) { |
| BusHolder itBus = iterator.next(); |
| if (bus == null || itBus == null || itBus.bus == null |
| || itBus.stale |
| || bus.equals(itBus.bus)) { |
| if (itBus != null) { |
| itBus.bus = null; |
| //mark as stale so if a thread asks again, it will create a new one |
| itBus.stale = true; |
| } |
| //This will remove the BusHolder from the only place that should |
| //strongly reference it |
| iterator.remove(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets the default bus if a default bus is not already set. |
| * |
| * @param bus the default bus. |
| * @return true if the bus was not set and is now set |
| */ |
| public static synchronized boolean possiblySetDefaultBus(Bus bus) { |
| BusHolder b = getThreadBusHolder(false); |
| if (b.bus == null) { |
| b.bus = bus; |
| } |
| if (defaultBus == null) { |
| defaultBus = bus; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Create a new BusFactory The class of the BusFactory is determined by looking for the system propery: |
| * org.apache.cxf.bus.factory or by searching the classpath for: |
| * META-INF/services/org.apache.cxf.bus.factory |
| * |
| * @return a new BusFactory to be used to create Bus objects |
| */ |
| public static BusFactory newInstance() { |
| return newInstance(null); |
| } |
| |
| /** |
| * Create a new BusFactory |
| * |
| * @param className The class of the BusFactory to create. If null, uses the default search algorithm. |
| * @return a new BusFactory to be used to create Bus objects |
| */ |
| public static BusFactory newInstance(String className) { |
| BusFactory instance = null; |
| if (className == null) { |
| ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| className = getBusFactoryClass(loader); |
| if (className == null && loader != BusFactory.class.getClassLoader()) { |
| className = getBusFactoryClass(BusFactory.class.getClassLoader()); |
| } |
| } |
| if (className == null) { |
| className = BusFactory.DEFAULT_BUS_FACTORY; |
| } |
| |
| Class<? extends BusFactory> busFactoryClass; |
| try { |
| busFactoryClass = ClassLoaderUtils.loadClass(className, BusFactory.class) |
| .asSubclass(BusFactory.class); |
| |
| instance = busFactoryClass.newInstance(); |
| } catch (Exception ex) { |
| LogUtils.log(LOG, Level.SEVERE, "BUS_FACTORY_INSTANTIATION_EXC", ex); |
| throw new RuntimeException(ex); |
| } |
| return instance; |
| } |
| |
| protected void initializeBus(Bus bus) { |
| } |
| |
| private static String getBusFactoryClass(ClassLoader classLoader) { |
| |
| String busFactoryClass = null; |
| String busFactoryCondition = null; |
| |
| // next check system properties |
| busFactoryClass = SystemPropertyAction.getPropertyOrNull(BusFactory.BUS_FACTORY_PROPERTY_NAME); |
| if (isValidBusFactoryClass(busFactoryClass)) { |
| return busFactoryClass; |
| } |
| |
| try { |
| // next, check for the services stuff in the jar file |
| String serviceId = "META-INF/services/" + BusFactory.BUS_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) { |
| serviceId = "META-INF/cxf/" + BusFactory.BUS_FACTORY_PROPERTY_NAME; |
| |
| if (classLoader == null) { |
| classLoader = Thread.currentThread().getContextClassLoader(); |
| } |
| |
| if (classLoader == null) { |
| is = ClassLoader.getSystemResourceAsStream(serviceId); |
| } else { |
| is = classLoader.getResourceAsStream(serviceId); |
| } |
| } |
| |
| if (is != null) { |
| try (BufferedReader rd = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { |
| busFactoryClass = rd.readLine(); |
| busFactoryCondition = rd.readLine(); |
| } |
| } |
| if (isValidBusFactoryClass(busFactoryClass) |
| && busFactoryCondition != null) { |
| try { |
| Class<?> cls = ClassLoaderUtils.loadClass(busFactoryClass, BusFactory.class) |
| .asSubclass(BusFactory.class); |
| if (busFactoryCondition.startsWith("#")) { |
| busFactoryCondition = busFactoryCondition.substring(1); |
| } |
| int idx = busFactoryCondition.indexOf(','); |
| while (idx != -1) { |
| cls.getClassLoader().loadClass(busFactoryCondition.substring(0, idx)); |
| busFactoryCondition = busFactoryCondition.substring(idx + 1); |
| idx = busFactoryCondition.indexOf(','); |
| } |
| cls.getClassLoader().loadClass(busFactoryCondition); |
| } catch (ClassNotFoundException e) { |
| busFactoryClass = DEFAULT_BUS_FACTORY; |
| } catch (NoClassDefFoundError e) { |
| busFactoryClass = DEFAULT_BUS_FACTORY; |
| } |
| |
| } |
| return busFactoryClass; |
| |
| } catch (Exception ex) { |
| LogUtils.log(LOG, Level.SEVERE, "FAILED_TO_DETERMINE_BUS_FACTORY_EXC", ex); |
| } |
| return busFactoryClass; |
| } |
| |
| private static boolean isValidBusFactoryClass(String busFactoryClassName) { |
| return busFactoryClassName != null && !"".equals(busFactoryClassName); |
| } |
| |
| } |