org.jboss.weld.probe.Probe Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of weld-servlet-shaded Show documentation
Show all versions of weld-servlet-shaded Show documentation
This jar bundles all the bits of Weld and CDI required for running in a Servlet container.
/*
* JBoss, Home of Professional Open Source
* Copyright 2015, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.jboss.weld.probe;
import static org.jboss.weld.probe.Strings.ADDITIONAL_BDA_SUFFIX;
import static org.jboss.weld.probe.Strings.WEB_INF_CLASSES;
import static org.jboss.weld.util.reflection.Reflections.cast;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.enterprise.inject.Vetoed;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Decorator;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.Interceptor;
import javax.enterprise.inject.spi.ObserverMethod;
import org.jboss.weld.Container;
import org.jboss.weld.bean.AbstractProducerBean;
import org.jboss.weld.bean.builtin.AbstractBuiltInBean;
import org.jboss.weld.bean.builtin.ExtensionBean;
import org.jboss.weld.bootstrap.spi.BeanDeploymentArchive;
import org.jboss.weld.bootstrap.spi.BeansXml;
import org.jboss.weld.bootstrap.spi.Metadata;
import org.jboss.weld.event.ObserverMethodImpl;
import org.jboss.weld.exceptions.IllegalStateException;
import org.jboss.weld.injection.attributes.WeldInjectionPointAttributes;
import org.jboss.weld.injection.producer.AbstractMemberProducer;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.probe.Components.BeanKind;
import org.jboss.weld.probe.Queries.BeanFilters;
import org.jboss.weld.probe.Queries.Filters;
import org.jboss.weld.probe.Queries.ObserverFilters;
import org.jboss.weld.serialization.spi.ContextualStore;
import org.jboss.weld.util.collections.SetMultimap;
import org.jboss.weld.util.reflection.Reflections;
/**
* This component holds all the mapping and monitoring data.
*
* @author Martin Kouba
*/
@Vetoed
class Probe {
// If needed make this configurable
private static final int DEFAULT_INVOCATIONS_LIMIT = 5000;
// If needed make this configurable
private static final int DEFAULT_EVENTS_LIMIT = 5000;
// Immutable mappings
private final Map, String> beanToId;
private final Map> idToBean;
private final Map, BeanManagerImpl> beanToManager;
private final Map> idToObserver;
private final Map, String> observerToId;
private final Map bdaToManager;
private final SetMultimap, AbstractProducerBean, ?, ?>> beanToDeclaredProducers;
private final Set> unusedBeans;
// Monitoring data
private final ConcurrentMap invocations;
private final List events;
// Comparators
private final Comparator> beanComparator;
private final Comparator> observerComparator;
private final Comparator bdaComparator;
private final AtomicLong initTs;
private final BootstrapStats bootstrapStats;
/**
*
*/
Probe() {
this.initTs = new AtomicLong(0);
this.invocations = new ConcurrentHashMap();
this.events = Collections.synchronizedList(new LinkedList());
this.beanToId = new HashMap, String>();
this.idToBean = new HashMap>();
this.beanToManager = new HashMap, BeanManagerImpl>();
this.idToObserver = new HashMap>();
this.observerToId = new HashMap, String>();
this.beanToDeclaredProducers = SetMultimap.newSetMultimap();
this.unusedBeans = new HashSet<>();
this.bdaToManager = new HashMap();
this.beanComparator = new Comparator>() {
@Override
public int compare(Bean> o1, Bean> o2) {
if (o1.getBeanClass().equals(o2.getBeanClass())) {
return beanToId.get(o1).compareTo(beanToId.get(o2));
}
return o1.getBeanClass().getName().compareTo(o2.getBeanClass().getName());
}
};
this.observerComparator = new Comparator>() {
@Override
public int compare(ObserverMethod> o1, ObserverMethod> o2) {
if (o1.getBeanClass().equals(o2.getBeanClass())) {
return observerToId.get(o1).compareTo(observerToId.get(o2));
}
return o1.getBeanClass().getName().compareTo(o2.getBeanClass().getName());
}
};
this.bdaComparator = new Comparator() {
@Override
public int compare(BeanDeploymentArchive bda1, BeanDeploymentArchive bda2) {
// Ids containing "WEB-INF/classes" have the highest priority
int result = Boolean.compare(bda2.getId().contains(WEB_INF_CLASSES), bda1.getId().contains(WEB_INF_CLASSES));
if (result == 0) {
// Additional bean archive should have the lowest priority when sorting
// This suffix is supported by WildFly and Weld Servlet
result = Boolean.compare(bda1.getId().endsWith(ADDITIONAL_BDA_SUFFIX), bda2.getId().endsWith(ADDITIONAL_BDA_SUFFIX));
if (result == 0) {
// Then order by number of enabled beans
result = Components.getNumberOfEnabledBeans(bdaToManager.get(bda2)).compareTo(
Components.getNumberOfEnabledBeans(bdaToManager.get(bda1)));
}
}
// Unless decided compare the ids lexicographically
return result == 0 ? bda1.getId().compareTo(bda2.getId()) : result;
}
};
this.bootstrapStats = new BootstrapStats();
}
/**
*
* @param beanManager
*/
void init(BeanManagerImpl beanManager) {
ContextualStore contextualStore = beanManager.getServices().get(ContextualStore.class);
bdaToManager.putAll(Container.instance(beanManager).beanDeploymentArchives());
for (Entry entry : bdaToManager.entrySet()) {
ProbeLogger.LOG.processingBeanDeploymentArchive(entry.getKey().getId());
BeanManagerImpl manager = entry.getValue();
// Beans
for (Bean> bean : manager.getBeans()) {
// Treat built-in beans (except for extensions) as one entity so that the dependency graph is more meaningful
// E.g. Weld registers a separate InstanceBean for every bean deployment archive, from the user point of view
// there's only one Instance bean though
if (bean instanceof ExtensionBean) {
// ExtensionBean does not include BeanManager in its BeanIdentifier
ExtensionBean> extensionBean = (ExtensionBean>) bean;
if (!idToBean.containsValue(extensionBean)) {
putBean(Components.getId(extensionBean.getIdentifier()), manager, extensionBean);
}
} else if (bean instanceof AbstractBuiltInBean>) {
// Built-in beans are identified by the set of types
String id = Components.getBuiltinBeanId((AbstractBuiltInBean>) bean);
if (!idToBean.containsKey(id)) {
putBean(id, bean);
}
} else {
if (manager.isBeanEnabled(bean)) {
// Make sure the bean is truly enabled
putBean(contextualStore, manager, bean);
}
}
}
// Interceptors
for (Interceptor> interceptor : manager.getInterceptors()) {
putBean(contextualStore, manager, interceptor);
}
// Decorators
for (Decorator> decorator : manager.getDecorators()) {
putBean(contextualStore, manager, decorator);
}
// Observers
int customObservers = 0;
for (ObserverMethod> observerMethod : manager.getObservers()) {
if (observerMethod instanceof ObserverMethodImpl) {
ObserverMethodImpl, ?> observerMethodImpl = (ObserverMethodImpl, ?>) observerMethod;
putObserver(Components.getId(observerMethodImpl.getId()), observerMethodImpl);
} else {
// Custom observer methods
putObserver(Components.getId("" + customObservers++), observerMethod);
}
}
}
// Find declared producers
for (Bean> candidate : idToBean.values()) {
BeanKind kind = BeanKind.from(candidate);
if ((BeanKind.PRODUCER_FIELD.equals(kind) || BeanKind.PRODUCER_METHOD.equals(kind) || BeanKind.RESOURCE.equals(kind))
&& candidate instanceof AbstractProducerBean) {
AbstractProducerBean, ?, ?> producerBean = (AbstractProducerBean, ?, ?>) candidate;
beanToDeclaredProducers.put(producerBean.getDeclaringBean(), producerBean);
}
}
findUnusedBeans();
initTs.set(System.currentTimeMillis());
}
/**
*
* @return an ordered list of all beans, including interceptors and decorators
*/
List> getBeans() {
List> data = new ArrayList>(idToBean.values());
Collections.sort(data, beanComparator);
return data;
}
/**
*
* @return an ordered list of beans
*/
List> getOrderedBeans(Set> beans) {
List> data = new ArrayList>(beans);
Collections.sort(data, beanComparator);
return data;
}
/**
*
* @param bean
* @return the generated id for the given bean
*/
String getBeanId(Bean> bean) {
return beanToId.get(bean);
}
/**
*
* @param id
* @return the bean for the given generated id
*/
Bean> getBean(String id) {
return idToBean.get(id);
}
/**
*
* @param bean
* @return the BeanManagerImpl for the given bean
*/
BeanManagerImpl getBeanManager(Bean> bean) {
return beanToManager.get(bean);
}
/**
*
* @return an ordered list of all observers
*/
List> getObservers() {
List> observers = new ArrayList>(idToObserver.values());
Collections.sort(observers, observerComparator);
return observers;
}
/**
*
* @param bean
* @return the generated id for the given observer method
*/
String getObserverId(ObserverMethod> observerMethod) {
return observerToId.get(observerMethod);
}
/**
*
* @param id
* @return the observer method for the given generated id
*/
ObserverMethod> getObserver(String id) {
return idToObserver.get(id);
}
/**
*
* @param bean
* @return the set of declared producers
*/
Set> getDeclaredProducers(Bean> bean) {
return beanToDeclaredProducers.containsKey(bean) ? beanToDeclaredProducers.get(bean) : Collections.emptySet();
}
/**
*
* @param invocation
*/
void addInvocation(Invocation invocation) {
if (!invocation.isEntryPoint()) {
throw new IllegalStateException("Invocation is not an entry point!");
}
// Remove some old data if the limit is exceeded
if (invocations.size() > DEFAULT_INVOCATIONS_LIMIT) {
synchronized (this) {
if (invocations.size() > DEFAULT_INVOCATIONS_LIMIT) {
Set keySet = invocations.keySet();
List sorted = new ArrayList(keySet);
Collections.sort(sorted, Collections.reverseOrder());
if (keySet.removeAll(sorted.subList(DEFAULT_INVOCATIONS_LIMIT / 2, sorted.size()))) {
ProbeLogger.LOG.monitoringLimitExceeded(Invocation.class.getSimpleName(), DEFAULT_INVOCATIONS_LIMIT);
}
}
}
}
invocations.put(invocation.getEntryPointIdx(), invocation);
}
/**
*
* @return the sorted entry points (invocation trees)
*/
List getInvocations() {
List sorted = new ArrayList(invocations.values());
Collections.sort(sorted, Invocation.Comparators.ENTRY_POINT_IDX);
return sorted;
}
/**
*
* @param id
* @return the invocation tree with the given generated id
*/
Invocation getInvocation(String id) {
try {
return invocations.get(Integer.valueOf(id));
} catch (NumberFormatException e) {
return null;
}
}
/**
*
*/
int clearInvocations() {
int size = invocations.size();
invocations.clear();
return size;
}
void addEvent(EventInfo event) {
// Remove some old data if the limit is exceeded
if (events.size() > DEFAULT_EVENTS_LIMIT) {
synchronized (this) {
if (events.size() > DEFAULT_EVENTS_LIMIT) {
events.subList(0, DEFAULT_EVENTS_LIMIT / 2).clear();
ProbeLogger.LOG.monitoringLimitExceeded(EventInfo.class.getSimpleName(), DEFAULT_EVENTS_LIMIT);
}
}
}
events.add(event);
}
/**
* Returns a mutable copy of the captured event information (in reverse order - last added events go first).
*
* @return mutable copy of the captured event information
*/
List getEvents() {
synchronized (events) {
List result = new ArrayList<>(events.size());
for (ListIterator iterator = events.listIterator(events.size()); iterator.hasPrevious();) {
result.add(iterator.previous());
}
return result;
}
}
/**
*
* @return the number of captured events before the state is cleared.
*/
int clearEvents() {
synchronized (events) {
int count = events.size();
events.clear();
return count;
}
}
/**
*
* @return the comparator used for beans
*/
Comparator> getBeanComparator() {
return beanComparator;
}
/**
*
* @return the comparator used for observer methods
*/
Comparator> getObserverComparator() {
return observerComparator;
}
/**
*
* @return the comparator used for bean archives
*/
Comparator getBdaComparator() {
return bdaComparator;
}
boolean isInitialized() {
return initTs.get() == 0 ? false : true;
}
long getInitTs() {
return initTs.get();
}
BootstrapStats getBootstrapStats() {
return bootstrapStats;
}
/**
*
* @param id
* @return the bean for the given generated id
*/
BeanManagerImpl getBeanManager(String id) {
for (Entry entry : bdaToManager.entrySet()) {
if (Components.getId(entry.getKey().getId()).equals(id)) {
return entry.getValue();
}
}
return null;
}
int getApplicationBeansCount() {
return Queries.find(getBeans(), 0, 0, new BeanFilters(this, Filters.FILTER_ADDITIONAL_BDAS_MARKER)).getTotal();
}
int getApplicationObserversCount() {
return Queries.find(getObservers(), 0, 0, new ObserverFilters(this, null, Filters.FILTER_ADDITIONAL_BDAS_MARKER)).getTotal();
}
int getInvocationsCount() {
return invocations.size();
}
int getFiredEventsCount() {
synchronized (events) {
return events.size();
}
}
boolean isUnused(Bean> bean) {
return unusedBeans.contains(bean);
}
private void putBean(ContextualStore contextualStore, Bean> bean) {
putBean(Components.getId(contextualStore.putIfAbsent(bean)), bean);
}
private void putBean(String id, Bean> bean) {
idToBean.put(id, bean);
beanToId.put(bean, id);
}
private void putBean(String id, BeanManagerImpl manager, Bean> bean) {
putBean(id, bean);
beanToManager.put(bean, manager);
}
private void putBean(ContextualStore contextualStore, BeanManagerImpl manager, Bean> bean) {
putBean(contextualStore, bean);
beanToManager.put(bean, manager);
}
private void putObserver(String id, ObserverMethod> observerMethod) {
idToObserver.put(id, observerMethod);
observerToId.put(observerMethod, id);
}
private void findUnusedBeans() {
Collection> beans = idToBean.values();
Collection> observers = idToObserver.values();
for (Bean> bean : beans) {
BeanKind kind = BeanKind.from(bean);
if (BeanKind.BUILT_IN.equals(kind) || BeanKind.EXTENSION.equals(kind) || BeanKind.DECORATOR.equals(kind) || BeanKind.INTERCEPTOR.equals(kind)) {
continue;
}
if (bean.getName() != null) {
// Is annotated with @Named
continue;
}
if (!(BeanKind.PRODUCER_FIELD.equals(kind) || BeanKind.PRODUCER_METHOD.equals(kind)) && !getDeclaredProducers(bean).isEmpty()) {
// Has declared producers
continue;
}
if (Components.hasDependents(bean, beans, this)) {
// Has direct or potential (Instance<>) dependents
continue;
}
if (hasDeclaredObserversOrIsInjectedIntoObserver(bean, observers)) {
continue;
}
if (isInjectedIntoDisposer(bean, beans)) {
continue;
}
unusedBeans.add(bean);
}
}
private boolean isInjectedIntoDisposer(Bean> bean, Collection> beans) {
for (Bean> producerCandidate : beans) {
if (producerCandidate instanceof AbstractProducerBean) {
AbstractProducerBean, ?, ?> producerBean = cast(producerCandidate);
if (producerBean.getProducer() instanceof AbstractMemberProducer, ?>) {
AbstractMemberProducer, ?> producer = Reflections.> cast(producerBean.getProducer());
if (producer.getDisposalMethod() != null) {
BeanManager beanManager = getBeanManager(bean);
for (InjectionPoint injectionPoint : producer.getDisposalMethod().getInjectionPoints()) {
if (bean.equals(beanManager.resolve(beanManager.getBeans(injectionPoint.getType(),
injectionPoint.getQualifiers().toArray(new Annotation[injectionPoint.getQualifiers().size()]))))) {
return true;
}
}
}
}
}
}
return false;
}
private boolean hasDeclaredObserversOrIsInjectedIntoObserver(Bean> bean, Collection> observers) {
for (ObserverMethod> observerMethod : observers) {
if (observerMethod instanceof ObserverMethodImpl) {
ObserverMethodImpl, ?> observerMethodImpl = (ObserverMethodImpl, ?>) observerMethod;
if (bean.equals(observerMethodImpl.getDeclaringBean())) {
return true;
}
Set> injectionPoints = observerMethodImpl.getInjectionPoints();
if (!injectionPoints.isEmpty()) {
BeanManager beanManager = getBeanManager(observerMethodImpl.getDeclaringBean());
if (beanManager != null) {
for (WeldInjectionPointAttributes, ?> injectionPoint : injectionPoints) {
if (bean.equals(beanManager.resolve(beanManager.getBeans(injectionPoint.getType(),
injectionPoint.getQualifiers().toArray(new Annotation[injectionPoint.getQualifiers().size()]))))) {
return true;
}
}
}
}
}
}
return false;
}
List getLocalEnablementOfBean(Class> clazz) {
List localEnablementBDAIds = new ArrayList<>();
for (Entry entry : bdaToManager.entrySet()) {
BeansXml beansXml = entry.getKey().getBeansXml();
if (beansXml != null) {
if (beansXml.getEnabledDecorators() != null && !beansXml.getEnabledDecorators().isEmpty()) {
for (Metadata metadata : entry.getKey().getBeansXml().getEnabledDecorators()) {
if (metadata.getValue().equals(clazz.getName())) {
localEnablementBDAIds.add(entry.getKey().getId());
}
}
}
if (beansXml.getEnabledInterceptors() != null && !beansXml.getEnabledInterceptors().isEmpty()) {
for (Metadata metadata : entry.getKey().getBeansXml().getEnabledInterceptors()) {
if (metadata.getValue().equals(clazz.getName())) {
localEnablementBDAIds.add(entry.getKey().getId());
}
}
}
}
}
return localEnablementBDAIds;
}
}