/*
* Copyright 2017 OmniFaces
*
* 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 org.omnifaces.cdi.eager;
import static java.lang.String.format;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;
import static org.omnifaces.util.Beans.getReference;
import static org.omnifaces.util.Utils.isAnyEmpty;
import static org.omnifaces.util.Utils.isEmpty;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import org.omnifaces.util.Utils;
/**
* Bean repository via which various types of eager beans can be instantiated on demand.
*
* @since 1.8
* @author Arjan Tijms
*
*/
@ApplicationScoped
public class EagerBeansRepository {
private static final Logger logger = Logger.getLogger(EagerBeansRepository.class.getName());
private static final String MISSING_REQUEST_URI_OR_VIEW_ID =
"Bean '%s' was annotated with @Eager, but required attribute 'requestURI' or 'viewId' is missing."
+ " Bean will not be eagerly instantiated.";
private static final String MISSING_VIEW_ID =
"Bean '%s' was annotated with @Eager, but required attribute 'viewId' is missing."
+ " Bean will not be eagerly instantiated.";
private static final String WARNING_POSSIBLY_APPLICATION_SCOPE_NOT_ACTIVE =
"Could not instantiate eager application scoped beans. Possibly the CDI application scope is not active."
+ " This is known to be the case in certain Tomcat and Jetty based configurations.";
private static volatile EagerBeansRepository instance;
@Inject
private BeanManager beanManager;
private EagerBeans eagerBeans;
public static EagerBeansRepository getInstance() { // Awkward workaround for it being unavailable via @Inject in listeners in Tomcat+OWB and Jetty.
if (instance == null) {
instance = getReference(EagerBeansRepository.class);
}
return instance;
}
protected void setEagerBeans(EagerBeans eagerBeans) {
this.eagerBeans = eagerBeans;
}
public static void instantiateApplicationScopedAndRegisterListenerIfNecessary(ServletContext servletContext) {
try {
if (getInstance() != null && instance.hasAnyApplicationScopedBeans()) { // #318: getInstance() should stay in try block.
instance.instantiateApplicationScoped();
}
}
catch (Exception e) {
logger.log(WARNING, format(WARNING_POSSIBLY_APPLICATION_SCOPE_NOT_ACTIVE), e);
instance = null; // Trigger to add listeners anyway as it may be available at later point.
}
if (instance == null || instance.hasAnySessionOrRequestURIBeans()) {
servletContext.addListener(EagerBeansWebListener.class);
}
}
protected boolean hasAnyApplicationScopedBeans() {
return eagerBeans != null && !isEmpty(eagerBeans.applicationScoped);
}
protected boolean hasAnySessionOrRequestURIBeans() {
return eagerBeans != null && (!isEmpty(eagerBeans.sessionScoped) || !isEmpty(eagerBeans.byRequestURI));
}
protected boolean hasAnyViewIdBeans() {
return eagerBeans != null && !isEmpty(eagerBeans.byViewId);
}
public void instantiateApplicationScoped() {
instantiateBeans(eagerBeans.applicationScoped);
}
public boolean instantiateSessionScoped() {
return eagerBeans != null && instantiateBeans(eagerBeans.sessionScoped);
}
public boolean instantiateByRequestURI(String relativeRequestURI) {
return eagerBeans != null && instantiateBeans(eagerBeans.byRequestURI, relativeRequestURI);
}
public void instantiateByViewID(String viewId) {
instantiateBeans(eagerBeans.byViewId, viewId);
}
private boolean instantiateBeans(Map<String, List<Bean<?>>> beansByKey, String key) {
if (isAnyEmpty(beansByKey, key)) {
return false;
}
instantiateBeans(beansByKey.get(key));
return true;
}
private boolean instantiateBeans(List<Bean<?>> beans) {
if (isAnyEmpty(beans, beanManager)) {
return false;
}
for (Bean<?> bean : beans) {
beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean)).toString();
}
return true;
}
protected static class EagerBeans {
private List<Bean<?>> applicationScoped = new ArrayList<>();
private List<Bean<?>> sessionScoped = new ArrayList<>();
private Map<String, List<Bean<?>>> byViewId = new ConcurrentHashMap<>();
private Map<String, List<Bean<?>>> byRequestURI = new ConcurrentHashMap<>();
void addApplicationScoped(Bean<?> bean) {
applicationScoped.add(bean);
}
void addSessionScoped(Bean<?> bean) {
sessionScoped.add(bean);
}
void addByViewId(Bean<?> bean, String viewId) {
if (!Utils.isEmpty(viewId)) {
getByViewId(viewId).add(bean);
}
else if (logger.isLoggable(SEVERE)) {
logger.log(SEVERE, format(MISSING_VIEW_ID, bean.getBeanClass().getName()));
}
}
void addByRequestURIOrViewId(Bean<?> bean, String requestURI, String viewId) {
if (!Utils.isEmpty(requestURI)) {
getByRequestURI(requestURI).add(bean);
}
else if (!Utils.isEmpty(viewId)) {
getByViewId(viewId).add(bean);
}
else if (logger.isLoggable(SEVERE)) {
logger.log(SEVERE, format(MISSING_REQUEST_URI_OR_VIEW_ID, bean.getBeanClass().getName()));
}
}
private List<Bean<?>> getByViewId(String viewId) {
List<Bean<?>> beans = byViewId.get(viewId);
if (beans == null) {
beans = new ArrayList<>();
byViewId.put(viewId, beans);
}
return beans;
}
private List<Bean<?>> getByRequestURI(String requestURI) {
List<Bean<?>> beans = byRequestURI.get(requestURI);
if (beans == null) {
beans = new ArrayList<>();
byRequestURI.put(requestURI, beans);
}
return beans;
}
public boolean isEmpty() {
return applicationScoped.isEmpty() && sessionScoped.isEmpty() && byViewId.isEmpty() && byRequestURI.isEmpty();
}
}
}