blob: d4ce0be17708ac488862783e1c968f97f92304ea [file] [log] [blame]
/**
* 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.bus.extension;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.cxf.Bus;
import org.apache.cxf.common.injection.ResourceInjector;
import org.apache.cxf.configuration.ConfiguredBeanLocator;
import org.apache.cxf.configuration.Configurer;
import org.apache.cxf.resource.ObjectTypeResolver;
import org.apache.cxf.resource.ResourceManager;
import org.apache.cxf.resource.ResourceResolver;
import org.apache.cxf.resource.SinglePropertyResolver;
public class ExtensionManagerImpl implements ExtensionManager, ConfiguredBeanLocator {
public static final String EXTENSIONMANAGER_PROPERTY_NAME = "extensionManager";
public static final String ACTIVATION_NAMESPACES_PROPERTY_NAME = "activationNamespaces";
public static final String ACTIVATION_NAMESPACES_SETTER_METHOD_NAME = "setActivationNamespaces";
public static final String BUS_EXTENSION_RESOURCE = "META-INF/cxf/bus-extensions.txt";
private final ClassLoader loader;
private ResourceManager resourceManager;
private Map<String, Extension> all = new ConcurrentHashMap<>();
private List<Extension> ordered = new CopyOnWriteArrayList<>();
private final Map<Class<?>, Object> activated;
private final Bus bus;
public ExtensionManagerImpl(ClassLoader cl, Map<Class<?>, Object> initialExtensions,
ResourceManager rm, Bus b) {
this(new String[] {BUS_EXTENSION_RESOURCE},
cl, initialExtensions, rm, b);
}
public ExtensionManagerImpl(String resource,
ClassLoader cl,
Map<Class<?>, Object> initialExtensions,
ResourceManager rm,
Bus b) {
this(new String[] {resource}, cl, initialExtensions, rm, b);
}
public ExtensionManagerImpl(String[] resources,
ClassLoader cl,
Map<Class<?>, Object> initialExtensions,
ResourceManager rm,
Bus b) {
loader = cl;
bus = b;
activated = initialExtensions;
resourceManager = rm;
ResourceResolver extensionManagerResolver =
new SinglePropertyResolver(EXTENSIONMANAGER_PROPERTY_NAME, this);
resourceManager.addResourceResolver(extensionManagerResolver);
resourceManager.addResourceResolver(new ObjectTypeResolver(this));
load(resources);
for (Map.Entry<String, Extension> ext : ExtensionRegistry.getRegisteredExtensions().entrySet()) {
if (!all.containsKey(ext.getKey())) {
all.put(ext.getKey(), ext.getValue());
ordered.add(ext.getValue());
}
}
}
final void load(String[] resources) {
if (resources == null) {
return;
}
try {
for (String resource : resources) {
load(resource);
}
} catch (IOException ex) {
throw new ExtensionException(ex);
}
}
public void add(Extension ex) {
all.put(ex.getName(), ex);
ordered.add(ex);
}
public void initialize() {
for (Extension e : ordered) {
if (!e.isDeferred() && e.getLoadedObject() == null) {
loadAndRegister(e);
}
}
}
public void removeBeansOfNames(List<String> names) {
for (String s : names) {
Extension ex = all.remove(s);
if (ex != null) {
ordered.remove(ex);
}
}
}
public void activateAll() {
for (Extension e : ordered) {
if (e.getLoadedObject() == null) {
loadAndRegister(e);
}
}
}
public <T> void activateAllByType(Class<T> type) {
for (Extension e : ordered) {
if (e.getLoadedObject() == null) {
Class<?> cls = e.getClassObject(loader);
if (cls != null && type.isAssignableFrom(cls)) {
synchronized (e) {
loadAndRegister(e);
}
}
}
}
}
public boolean hasBeanOfName(String name) {
return all.containsKey(name);
}
final void load(String resource) throws IOException {
if (loader != getClass().getClassLoader()) {
load(resource, getClass().getClassLoader());
}
load(resource, loader);
}
final synchronized void load(String resource, ClassLoader l) throws IOException {
Enumeration<URL> urls = l.getResources(resource);
while (urls.hasMoreElements()) {
final URL url = urls.nextElement();
try (InputStream is = AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() {
public InputStream run() throws Exception {
return url.openStream();
}
})) {
List<Extension> exts = new TextExtensionFragmentParser(loader).getExtensions(is);
for (Extension e : exts) {
if (loader != l) {
e.classloader = l;
}
if (!all.containsKey(e.getName())) {
all.put(e.getName(), e);
ordered.add(e);
}
}
} catch (PrivilegedActionException pae) {
throw (IOException)pae.getException();
}
}
}
final void loadAndRegister(Extension e) {
Class<?> cls;
if (null != e.getInterfaceName() && !"".equals(e.getInterfaceName())) {
cls = e.loadInterface(loader);
} else {
cls = e.getClassObject(loader);
}
if (null != activated && null != cls && null != activated.get(cls)) {
return;
}
synchronized (e) {
Object obj = e.load(loader, bus);
if (obj == null) {
return;
}
if (null != activated) {
Configurer configurer = (Configurer)(activated.get(Configurer.class));
if (null != configurer) {
configurer.configureBean(obj);
}
}
// let the object know for which namespaces it has been activated
ResourceResolver namespacesResolver = null;
if (null != e.getNamespaces()) {
namespacesResolver = new SinglePropertyResolver(ACTIVATION_NAMESPACES_PROPERTY_NAME,
e.getNamespaces());
resourceManager.addResourceResolver(namespacesResolver);
}
// Since we need to support spring2.5 by removing @Resource("activationNamespaces")
// Now we call the setActivationNamespaces method directly here
if (e.getNamespaces() != null && !e.getNamespaces().isEmpty()) {
invokeSetterActivationNSMethod(obj, e.getNamespaces());
}
ResourceInjector injector = new ResourceInjector(resourceManager);
try {
injector.inject(obj);
injector.construct(obj);
} finally {
if (null != namespacesResolver) {
resourceManager.removeResourceResolver(namespacesResolver);
}
}
if (null != activated) {
if (cls == null) {
cls = obj.getClass();
}
activated.put(cls, obj);
}
}
}
public <T> T getExtension(String name, Class<T> type) {
if (name == null) {
return null;
}
Extension e = all.get(name);
if (e != null) {
Class<?> cls = e.getClassObject(loader);
if (cls != null && type.isAssignableFrom(cls)) {
synchronized (e) {
if (e.getLoadedObject() == null) {
loadAndRegister(e);
}
return type.cast(e.getLoadedObject());
}
}
}
return null;
}
private void invokeSetterActivationNSMethod(Object target, Object value) {
Class<?> clazz = target.getClass();
String methodName = ACTIVATION_NAMESPACES_SETTER_METHOD_NAME;
while (clazz != Object.class) {
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
Class<?>[] params = method.getParameterTypes();
if (method.getName().equals(methodName) && params.length == 1) {
Class<?> paramType = params[0];
if (paramType.isInstance(value)) {
try {
method.invoke(target, new Object[] {value});
} catch (Exception e) {
// do nothing here
}
return;
}
}
}
clazz = clazz.getSuperclass();
}
}
public List<String> getBeanNamesOfType(Class<?> type) {
List<String> ret = new LinkedList<>();
for (Extension ex : ordered) {
Class<?> cls = ex.getClassObject(loader);
if (cls != null && type.isAssignableFrom(cls)) {
synchronized (ex) {
ret.add(ex.getName());
}
}
}
return ret;
}
public <T> T getBeanOfType(String name, Class<T> type) {
if (name == null) {
return null;
}
Extension ex = all.get(name);
if (ex != null) {
if (ex.getLoadedObject() == null) {
loadAndRegister(ex);
}
return type.cast(ex.getLoadedObject());
}
return null;
}
public <T> Collection<? extends T> getBeansOfType(Class<T> type) {
List<T> ret = new LinkedList<>();
Extension ext = all.get(type.getName());
if (ext != null) {
Class<?> cls = ext.getClassObject(loader);
if (cls != null && type.isAssignableFrom(cls)) {
synchronized (ext) {
if (ext.getLoadedObject() == null) {
loadAndRegister(ext);
}
if (ext.getLoadedObject() != null) {
ret.add(type.cast(ext.getLoadedObject()));
}
}
}
}
for (Extension ex : ordered) {
if (ex != ext) {
Class<?> cls = ex.getClassObject(loader);
if (cls != null && type.isAssignableFrom(cls)) {
synchronized (ex) {
if (ex.getLoadedObject() == null) {
loadAndRegister(ex);
}
if (ex.getLoadedObject() != null) {
ret.add(type.cast(ex.getLoadedObject()));
}
}
}
}
}
return ret;
}
public <T> boolean loadBeansOfType(Class<T> type, BeanLoaderListener<T> listener) {
boolean loaded = false;
for (Extension ex : ordered) {
Class<?> cls = ex.getClassObject(loader);
if (cls != null
&& type.isAssignableFrom(cls)) {
synchronized (ex) {
if (listener.loadBean(ex.getName(), cls.asSubclass(type))) {
if (ex.getLoadedObject() == null) {
loadAndRegister(ex);
}
if (listener.beanLoaded(ex.getName(), type.cast(ex.getLoadedObject()))) {
return true;
}
loaded = true;
}
}
}
}
return loaded;
}
public boolean hasConfiguredPropertyValue(String beanName, String propertyName, String value) {
if (beanName == null) {
return false;
}
Extension ex = all.get(beanName);
return ex != null && ex.getNamespaces() != null
&& ex.getNamespaces().contains(value);
}
public void destroyBeans() {
for (Extension ex : ordered) {
if (ex.getLoadedObject() != null) {
ResourceInjector injector = new ResourceInjector(resourceManager);
injector.destroy(ex.getLoadedObject());
}
}
}
}