/*
* Copyright 2015 byteslounge.com (Gonçalo Marques).
*
* Licensed 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 com.byteslounge.cdi.resolver.bean;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.byteslounge.cdi.exception.ExtensionInitializationException;
/**
* Lazy resolver bean initializer created to solve bean resolution problems
* in older versions of Weld (ex: Weld 1.1.3 that is shipped with Weblogic 12.1.1).
*
* Basically we fall back to the Bean Manager that is fetched through JNDI in case
* the Bean Manager provided to the extension fails to resolve the Property/Locale resolvers.
*
* @author Gonçalo Marques
* @since 1.1.1
*/
public class ResolverInstanceLazyInitializer {
private static final Logger logger = LoggerFactory.getLogger(ResolverInstanceLazyInitializer.class);
private volatile ResolverInstance resolverInstance;
private final Class<?> resolverClass;
private final BeanManagerList beanManagers;
public ResolverInstanceLazyInitializer(BeanManager beanManager, Class<?> resolverClass) {
this.beanManagers = new BeanManagerList(beanManager);
this.resolverClass = resolverClass;
}
public ResolverInstance get() {
ResolverInstance result = resolverInstance;
if (result == null) {
synchronized (this) {
result = resolverInstance;
if (result == null) {
BeanResolverResult beanResolverResult = CDIBeanResolver.resolve(beanManagers, resolverClass);
BeanManager activeBeanManager = beanResolverResult.activeBeanManager;
Bean<?> resolverBean = activeBeanManager.resolve(beanResolverResult.beans);
if (resolverBean == null) {
throw new ExtensionInitializationException(
"Could not resolve bean for class: "
+ resolverClass.getName()
+ ". The class is probably deployed in a module that is not accessible by the CDI Properties extension classloader."
+ " Try to deploy the resolver class in a library JAR instead.");
}
CreationalContext<?> creationalContext = activeBeanManager.createCreationalContext(resolverBean);
resolverInstance = result = new ResolverInstance(activeBeanManager.getReference(resolverBean,
resolverClass, creationalContext), resolverBean, activeBeanManager);
}
}
}
return result;
}
public static class ResolverInstance {
private final Object resolverInstance;
private final Bean<?> resolverBean;
private final BeanManager activeBeanManager;
public ResolverInstance(Object resolverInstance, Bean<?> resolverBean,BeanManager activeBeanManager) {
this.resolverInstance = resolverInstance;
this.resolverBean = resolverBean;
this.activeBeanManager = activeBeanManager;
}
public Object getResolverInstance() {
return resolverInstance;
}
public Bean<?> getResolverBean() {
return resolverBean;
}
public BeanManager getActiveBeanManager() {
return activeBeanManager;
}
}
static class CDIBeanResolver {
static BeanResolverResult resolve(Iterable<BeanManager> beanManagers, Class<?> beanClass) {
Set<Bean<?>> beans = Collections.emptySet();
Iterator<BeanManager> it = beanManagers.iterator();
BeanManager activeBeanManager = null;
while (it.hasNext()) {
activeBeanManager = it.next();
beans = activeBeanManager.getBeans(beanClass);
if (!beans.isEmpty()) {
break;
}
}
return new BeanResolverResult(beans, activeBeanManager);
}
}
static class BeanResolverResult {
private final Set<Bean<?>> beans;
private final BeanManager activeBeanManager;
public BeanResolverResult(Set<Bean<?>> beans, BeanManager activeBeanManager) {
this.beans = beans;
this.activeBeanManager = activeBeanManager;
}
}
private static class BeanManagerList implements Iterable<BeanManager> {
private final BeanManager[] providedBeanManagers;
BeanManagerList(BeanManager... providedBeanManagers) {
this.providedBeanManagers = providedBeanManagers;
}
@Override
public Iterator<BeanManager> iterator() {
return new BeanManagerIterator();
}
private class BeanManagerIterator implements Iterator<BeanManager> {
private static final String BEAN_MANAGER_JNDI = "java:comp/BeanManager";
private int currentIndex = 0;
private BeanManager jndiBeanManager;
BeanManagerIterator() {
try {
jndiBeanManager = (BeanManager) new InitialContext().lookup(BEAN_MANAGER_JNDI);
} catch (NamingException e) {
logger.warn("Could not fetch Bean Manager from JNDI. Fallback strategy will not be used.", e);
}
}
@Override
public boolean hasNext() {
return currentIndex < providedBeanManagers.length || jndiBeanManager != null;
}
@Override
public BeanManager next() {
if (currentIndex < providedBeanManagers.length) {
return providedBeanManagers[currentIndex++];
}
if (jndiBeanManager == null) {
throw new NoSuchElementException();
}
BeanManager result = jndiBeanManager;
jndiBeanManager = null;
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not implemented");
}
}
}
}