/*
* Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.config.xml;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashSet;
import javax.ejb.Startup;
import javax.ejb.Stateless;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Stereotype;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.PassivationCapable;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.ProcessInjectionTarget;
import com.caucho.config.Config;
import com.caucho.config.ConfigException;
import com.caucho.config.ServiceStartup;
import com.caucho.config.bytecode.ScopeProxy;
import com.caucho.config.cfg.BeansConfig;
import com.caucho.config.extension.ProcessBeanImpl;
import com.caucho.config.inject.HandleAware;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.inject.ManagedBeanImpl;
import com.caucho.config.inject.ScheduleBean;
import com.caucho.config.inject.SingletonHandle;
import com.caucho.inject.LazyExtension;
import com.caucho.inject.Module;
import com.caucho.vfs.Path;
/**
* Standard XML behavior for META-INF/beans.xml
*/
@Module
public class XmlStandardPlugin implements Extension
{
private static final String SCHEMA = "com/caucho/config/cfg/resin-beans.rnc";
private InjectManager _cdiManager;
private HashSet<String> _configuredBeans = new HashSet<String>();
private ArrayList<Path> _paths = new ArrayList<Path>();
private ArrayList<Path> _pendingPaths = new ArrayList<Path>();
private ArrayList<BeansConfig> _pendingBeans = new ArrayList<BeansConfig>();
private ArrayList<Bean<?>> _pendingService = new ArrayList<Bean<?>>();
private Throwable _configException;
public XmlStandardPlugin(InjectManager manager)
{
_cdiManager = manager;
Thread.currentThread().getContextClassLoader();
}
public void addRoot(Path root)
{
if (! _paths.contains(root)) {
_pendingPaths.add(root);
}
}
public void beforeDiscovery(@Observes BeforeBeanDiscovery event)
{
ArrayList<Path> paths = new ArrayList<Path>(_pendingPaths);
_pendingPaths.clear();
try {
for (Path root : paths) {
configureRoot(root);
}
for (int i = 0; i < _pendingBeans.size(); i++) {
BeansConfig config = _pendingBeans.get(i);
ArrayList<Class<?>> deployList = config.getDeployList();
if (deployList != null && deployList.size() > 0) {
_cdiManager.setDeploymentTypes(deployList);
}
}
} catch (Exception e) {
if (_configException == null)
_configException = e;
throw ConfigException.create(e);
}
}
private void configureRoot(Path root)
throws IOException
{
configurePath(root.lookup("META-INF/beans.xml"));
configurePath(root.lookup("META-INF/resin-beans.xml"));
if (root.getFullPath().endsWith("WEB-INF/classes/")) {
configurePath(root.lookup("../beans.xml"));
configurePath(root.lookup("../resin-beans.xml"));
}
else if (! root.lookup("META-INF/beans.xml").canRead()
&& ! root.lookup("META-INF/resin-beans.xml").canRead()) {
// ejb/11h0
configurePath(root.lookup("beans.xml"));
configurePath(root.lookup("resin-beans.xml"));
}
}
private void configurePath(Path beansPath)
throws IOException
{
if (beansPath.canRead() && beansPath.getLength() > 0) {
// ioc/0041 - tck allows empty beans.xml
BeansConfig beans = new BeansConfig(_cdiManager, beansPath);
beansPath.setUserPath(beansPath.getURL());
new Config().configure(beans, beansPath, SCHEMA);
_pendingBeans.add(beans);
}
}
public void addConfiguredBean(String className)
{
_configuredBeans.add(className);
}
@LazyExtension
public void processType(@Observes ProcessAnnotatedType<?> event)
{
AnnotatedType<?> type = event.getAnnotatedType();
if (type == null)
return;
if (type.isAnnotationPresent(XmlCookie.class))
return;
if (_configuredBeans.contains(type.getJavaClass().getName())) {
event.veto();
return;
}
// XXX: managed by ResinStandardPlugin
/*
if (type.isAnnotationPresent(Stateful.class)
|| type.isAnnotationPresent(Stateless.class)
|| type.isAnnotationPresent(MessageDriven.class)) {
event.veto();
}
*/
}
@LazyExtension
public void processTarget(@Observes ProcessInjectionTarget<?> event)
{
AnnotatedType<?> type = event.getAnnotatedType();
XmlCookie cookie = type.getAnnotation(XmlCookie.class);
if (cookie != null) {
InjectionTarget target = _cdiManager.getXmlInjectionTarget(cookie.value());
event.setInjectionTarget(target);
}
}
public void processType(@Observes AfterBeanDiscovery event)
{
if (_configException != null)
event.addDefinitionError(_configException);
}
@LazyExtension
public void processBean(@Observes ProcessBean<?> event)
{
ProcessBeanImpl<?> eventImpl = (ProcessBeanImpl<?>) event;
if (eventImpl.getManager() != _cdiManager)
return;
Annotated annotated = event.getAnnotated();
Bean<?> bean = event.getBean();
if (isStartup(annotated)) {
_pendingService.add(bean);
}
}
public void processAfterValidation(@Observes AfterDeploymentValidation event)
{
ArrayList<Bean<?>> startupBeans = new ArrayList<Bean<?>>(_pendingService);
_pendingService.clear();
for (Bean<?> bean : startupBeans) {
CreationalContext<?> env = _cdiManager.createCreationalContext(bean);
Object value = _cdiManager.getReference(bean, bean.getBeanClass(), env);
if (value instanceof ScopeProxy)
((ScopeProxy) value).__caucho_getDelegate();
if (bean instanceof ScheduleBean) {
((ScheduleBean) bean).scheduleTimers(value);
}
if (value instanceof HandleAware && bean instanceof PassivationCapable) {
String id = ((PassivationCapable) bean).getId();
((HandleAware) value).setSerializationHandle(new SingletonHandle(id));
}
}
}
private boolean isStartup(Annotated annotated)
{
if (annotated == null)
return false;
for (Annotation ann : annotated.getAnnotations()) {
Class<?> annType = ann.annotationType();
// @Stateless must be on the bean itself
if (annType.equals(Stateless.class))
return true;
if (annType.equals(Startup.class))
return true;
if (annType.equals(ServiceStartup.class))
return true;
// @Startup & @ServiceStartup can be stereotyped
if (annType.isAnnotationPresent(Stereotype.class)) {
if (annType.isAnnotationPresent(ServiceStartup.class))
return true;
if (annType.isAnnotationPresent(Startup.class))
return true;
}
}
return false;
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[]";
}
}