/*
* 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.deltaspike.core.util;
import javax.enterprise.inject.spi.Extension;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.deltaspike.core.api.config.base.CoreBaseConfig;
/**
* Support for Containers with 'hierarchic BeanManagers'
* This is mostly useful for EAR applications.
*
* Some EE Container scan the common shared EAR lib path and reuse this information
* for the webapps in the EAR. This is actually the only approach a container can
* do to prevent mem leaks and side effects spreading to different webapps.
* Of course this also means that the webapps get their own (different)
* instances of an Extension.
*
* To acknowledge this solution we provide a mechanism to lookup 'parent Extensions'
* which is very similar to handling parent ClassLoaders.
*
* Please note that you need to enable this handling if you are running DeltaSpike
* in an EAR on a container which supports parent Extensions.
* You can do that by settting {@code "deltaspike.parent.extension.enabled"} to "e;true"e;
*
* All your Extension has to do is to register itself in
* {@link javax.enterprise.inject.spi.BeforeBeanDiscovery}.
* Later at boot time the Extension can lookup it's parent Extension instance and
* e.g. check which classes got scanned in the parent ClassLoader.
*
* The ExtensionInfo automatically gets removed if the webapp gets undeployed.
*
* @see org.apache.deltaspike.core.api.config.base.CoreBaseConfig.ParentExtensionCustomization
*/
public final class ParentExtensionStorage
{
private static Set<ExtensionStorageInfo> extensionStorage = new HashSet<ExtensionStorageInfo>();
private ParentExtensionStorage()
{
// utility class ct
}
/**
* Add info about an Extension to our storage
* This method is usually called during boostrap via {@code @Observes BeforeBeanDiscovery}.
*/
public static synchronized void addExtension(Extension extension)
{
if (usingParentExtension())
{
removeAbandonedExtensions();
ClassLoader classLoader = ClassUtils.getClassLoader(null);
extensionStorage.add(new ExtensionStorageInfo(classLoader, extension));
}
}
/**
* When adding a new Extension we also clean up ExtensionInfos
* from ClassLoaders which got unloaded.
*/
private static boolean usingParentExtension()
{
return CoreBaseConfig.ParentExtensionCustomization.PARENT_EXTENSION_ENABLED;
}
private static void removeAbandonedExtensions()
{
Iterator<ExtensionStorageInfo> it = extensionStorage.iterator();
while (it.hasNext())
{
ExtensionStorageInfo info = it.next();
if (info.isAbandoned())
{
it.remove();
}
}
}
/**
* @return the Extension from the same type but registered in a hierarchic 'parent' BeanManager
*/
public static synchronized <T extends Extension> T getParentExtension(Extension extension)
{
if (usingParentExtension())
{
ClassLoader parentClassLoader = ClassUtils.getClassLoader(null).getParent();
Iterator<ExtensionStorageInfo> extIt = extensionStorage.iterator();
while (extIt.hasNext())
{
ExtensionStorageInfo extensionInfo = extIt.next();
if (!extensionInfo.isAbandoned() && // weak reference case
extension.getClass().equals(extensionInfo.getExtension().getClass()) &&
extensionInfo.getClassLoader().equals(parentClassLoader))
{
return (T) extensionInfo.getExtension();
}
}
}
return null;
}
/**
* Information about an Extension instance and in which classloader it got used
*/
private static class ExtensionStorageInfo
{
// we use WeakReferences to allow perfect unloading of any webapp ClassLoader
private final WeakReference<ClassLoader> classLoader;
private final WeakReference<Extension> extension;
public ExtensionStorageInfo(ClassLoader classLoader, Extension extension)
{
this.classLoader = new WeakReference<ClassLoader>(classLoader);
this.extension = new WeakReference<Extension>(extension);
}
boolean isAbandoned()
{
return classLoader.get() == null || extension.get() == null;
}
ClassLoader getClassLoader()
{
return classLoader.get();
}
Extension getExtension()
{
return extension.get();
}
}
}