blob: 822674a8ac905d4d32524b745e141603791b9388 [file] [log] [blame]
/*
* 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.openejb;
import org.apache.openejb.assembler.classic.AppInfo;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.cdi.OWBInjector;
import org.apache.openejb.config.AppModule;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.config.ConnectorModule;
import org.apache.openejb.config.EjbModule;
import org.apache.openejb.config.NewLoaderLogic;
import org.apache.openejb.config.PersistenceModule;
import org.apache.openejb.config.ValidationFailedException;
import org.apache.openejb.jee.Application;
import org.apache.openejb.jee.Beans;
import org.apache.openejb.jee.Connector;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.EnterpriseBean;
import org.apache.openejb.jee.ManagedBean;
import org.apache.openejb.jee.TransactionType;
import org.apache.openejb.jee.jpa.unit.Persistence;
import org.apache.openejb.jee.jpa.unit.PersistenceUnit;
import org.apache.openejb.jee.oejb3.EjbDeployment;
import org.apache.openejb.jee.oejb3.OpenejbJar;
import org.apache.openejb.loader.Options;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.spi.ContainerSystem;
import org.apache.openejb.util.Exceptions;
import org.apache.openejb.util.Join;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import org.apache.openejb.util.OptionsLog;
import org.apache.openejb.util.ServiceManagerProxy;
import org.apache.xbean.naming.context.ContextFlyweight;
import javax.ejb.EJBException;
import javax.ejb.embeddable.EJBContainer;
import javax.ejb.spi.EJBContainerProvider;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.validation.ValidationException;
import java.io.File;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @version $Rev$ $Date$
*/
public class OpenEjbContainer extends EJBContainer {
public static final String OPENEJB_EMBEDDED_REMOTABLE = "openejb.embedded.remotable";
static Logger logger = Logger.getInstance(LogCategory.OPENEJB_STARTUP, OpenEjbContainer.class);
private static OpenEjbContainer instance;
private final AppContext appContext;
private ServiceManagerProxy serviceManager;
private Options options;
private OpenEjbContainer.GlobalContext globalJndiContext;
private OpenEjbContainer(Map<?, ?> map, AppContext appContext) {
this.appContext = appContext;
this.globalJndiContext = new GlobalContext(appContext.getGlobalJndiContext());
final Properties properties = new Properties();
properties.putAll(map);
options = new Options(properties);
startNetworkServices();
}
@Override
public void close() {
if (serviceManager != null) {
serviceManager.stop();
}
try {
globalJndiContext.close();
} catch (NamingException e) {
throw new IllegalStateException(e);
}
OpenEJB.destroy();
instance = null;
}
@Override
public Context getContext() {
return globalJndiContext;
}
public <T> T inject(T object) {
assert object != null;
final Class<?> clazz = object.getClass();
final BeanContext context = resolve(clazz);
if (context == null) throw new NoInjectionMetaDataException(clazz.getName());
final InjectionProcessor processor = new InjectionProcessor(object, context.getInjections(), context.getJndiContext());
try {
OWBInjector beanInjector = new OWBInjector(appContext.getWebBeansContext());
beanInjector.inject(object);
} catch (Throwable t) {
// TODO handle this differently
// this is temporary till the injector can be rewritten
}
try {
return (T) processor.createInstance();
} catch (OpenEJBException e) {
throw new InjectionException(clazz.getName(), e);
}
}
private <T> BeanContext resolve(Class<?> clazz) {
final ContainerSystem containerSystem = SystemInstance.get().getComponent(ContainerSystem.class);
while (clazz != null && clazz != Object.class) {
{
final BeanContext context = containerSystem.getBeanContext(clazz.getName());
if (context != null) return context;
}
for (BeanContext context : containerSystem.deployments()) {
if (clazz == context.getBeanClass()) return context;
}
clazz = clazz.getSuperclass();
}
return null;
}
private void startNetworkServices() {
if (!options.get(OPENEJB_EMBEDDED_REMOTABLE, false)) {
return;
}
try {
serviceManager = new ServiceManagerProxy();
serviceManager.start();
} catch (ServiceManagerProxy.AlreadyStartedException e) {
logger.debug("Network services already started. Ignoring option " + OPENEJB_EMBEDDED_REMOTABLE);
}
}
public static class NoInjectionMetaDataException extends IllegalStateException {
public NoInjectionMetaDataException(String s) {
super(s);
}
}
public static class InjectionException extends IllegalStateException {
public InjectionException(String message, Throwable cause) {
super(message, cause);
}
}
public static class Provider implements EJBContainerProvider {
public static final String OPENEJB_ADDITIONNAL_CALLERS_KEY = "openejb.additionnal.callers";
@Override
public EJBContainer createEJBContainer(Map<?, ?> map) {
if (isOtherProvider(map)) return null;
if (instance != null || OpenEJB.isInitialized()) {
logger.info("EJBContainer already initialized. Call ejbContainer.close() to allow reinitialization");
return instance;
}
try {
// reset to be able to run this container then tomee one etc...
if (System.getProperties().containsKey(Context.URL_PKG_PREFIXES)) {
System.getProperties().remove(Context.URL_PKG_PREFIXES);
}
Properties properties = new Properties();
properties.putAll(map);
SystemInstance.reset();
SystemInstance.init(properties);
SystemInstance.get().setProperty("openejb.embedded", "true");
SystemInstance.get().setProperty(EJBContainer.class.getName(), "true");
OptionsLog.install();
OpenEJB.init(properties);
final ConfigurationFactory configurationFactory = new ConfigurationFactory();
final AppModule appModule = load(map, configurationFactory);
final Set<String> callers;
if (map.containsKey(OPENEJB_ADDITIONNAL_CALLERS_KEY)) {
callers = new LinkedHashSet<String>();
callers.addAll(Arrays.asList(((String) map.get(OPENEJB_ADDITIONNAL_CALLERS_KEY)).split(",")));
} else {
callers = NewLoaderLogic.callers();
}
final EjbJar ejbJar = new EjbJar();
final OpenejbJar openejbJar = new OpenejbJar();
for (String caller : callers) {
if (!isValid(caller)) continue;
final ManagedBean bean = ejbJar.addEnterpriseBean(new ManagedBean(caller, caller, true));
// set it to bean so it can get UserTransaction injection
bean.setTransactionType(TransactionType.BEAN);
final EjbDeployment ejbDeployment = openejbJar.addEjbDeployment(bean);
// important in case any other deploment id formats are specified
ejbDeployment.setDeploymentId(caller);
}
appModule.getEjbModules().add(new EjbModule(ejbJar, openejbJar));
final AppInfo appInfo;
try {
appInfo = configurationFactory.configureApplication(appModule);
} catch (ValidationFailedException e) {
logger.warning("configureApplication.loadFailed", appModule.getModuleId(), e.getMessage()); // DO not include the stacktrace in the message
throw new InvalidApplicationException(e);
} catch (OpenEJBException e) {
// DO NOT REMOVE THE EXCEPTION FROM THIS LOG MESSAGE
// removing this message causes NO messages to be printed when embedded
logger.warning("configureApplication.loadFailed", e, appModule.getModuleId(), e.getMessage());
throw new ConfigureApplicationException(e);
}
final Assembler assembler = SystemInstance.get().getComponent(Assembler.class);
final AppContext appContext;
try {
appContext = assembler.createApplication(appInfo, appModule.getClassLoader());
} catch (ValidationException ve) {
throw ve;
} catch (Exception e) {
throw new AssembleApplicationException(e);
}
return instance = new OpenEjbContainer(map, appContext);
} catch (OpenEJBException e) {
throw new EJBException(e);
} catch (MalformedURLException e) {
throw new EJBException(e);
} catch (ValidationException ve) {
throw ve;
} catch (Exception e) {
if (e instanceof EJBException) {
throw (EJBException) e;
}
throw new InitializationException(e);
} finally {
if (instance == null && OpenEJB.isInitialized()) {
try {
OpenEJB.destroy();
} catch (Exception e) {
}
}
}
}
private boolean isValid(String caller) {
try {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
final Class<?> clazz = loader.loadClass(caller);
if (clazz.isEnum()) return false;
if (clazz.isInterface()) return false;
if (Modifier.isAbstract(clazz.getModifiers())) return false;
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
private AppModule load(Map<?, ?> map, ConfigurationFactory configurationFactory) throws MalformedURLException, OpenEJBException {
final List<File> moduleLocations;
String appId = (String) map.get(EJBContainer.APP_NAME);
final Object modules = map.get(EJBContainer.MODULES);
// will be updated if needed
ClassLoader classLoader = getClass().getClassLoader();
if (modules instanceof String) {
moduleLocations = configurationFactory.getModulesFromClassPath(null, classLoader);
for (Iterator<File> i = moduleLocations.iterator(); i.hasNext(); ) {
File file = i.next();
if (!match((String) modules, file)) {
i.remove();
}
}
} else if (modules instanceof String[]) {
// TODO Optimize this so we look specifically for modules by name
moduleLocations = configurationFactory.getModulesFromClassPath(null, classLoader);
int matched = 0;
for (Iterator<File> i = moduleLocations.iterator(); i.hasNext(); ) {
File file = i.next();
boolean remove = true;
for (String s : (String[]) modules) {
if (match(s, file)) {
remove = false;
matched++;
break;
}
}
if (remove) {
i.remove();
}
}
if (matched != ((String[]) modules).length) {
throw specifiedModulesNotFound();
}
} else if (modules instanceof File) {
URL url = ((File) modules).toURI().toURL();
classLoader = new URLClassLoader(new URL[]{url}, classLoader);
moduleLocations = Collections.singletonList((File) modules);
} else if (modules instanceof File[]) {
File[] files = (File[]) modules;
URL[] urls = new URL[files.length];
for (int i = 0; i < urls.length; i++) {
urls[i] = files[i].toURI().toURL();
}
classLoader = new URLClassLoader(urls, classLoader);
moduleLocations = Arrays.asList((File[]) modules);
} else if (modules == null) {
moduleLocations = configurationFactory.getModulesFromClassPath(null, classLoader);
} else {
AppModule appModule = load(map);
if (appModule != null) return appModule;
throw invalidModulesValue(modules);
}
if (moduleLocations.isEmpty()) {
throw Exceptions.newNoModulesFoundException();
}
// TODO With this load method we can finally do some checking on module ids to really implement EJBContainer.MODULES String/String[]
return configurationFactory.loadApplication(classLoader, appId, moduleLocations);
}
private AppModule load(Map<?, ?> map) {
String appId = (String) map.get(EJBContainer.APP_NAME);
final Object modules = map.get(EJBContainer.MODULES);
map.size();
AppModule m;
{
final Object obj = modules;
Application application = null;
AppModule appModule = new AppModule(this.getClass().getClassLoader(), appId);
{
if (obj instanceof EjbJar) {
final EjbJar ejbJar = (EjbJar) obj;
appModule.getEjbModules().add(new EjbModule(ejbJar));
} else if (obj instanceof EnterpriseBean) {
final EnterpriseBean bean = (EnterpriseBean) obj;
final EjbJar ejbJar = new EjbJar();
ejbJar.addEnterpriseBean(bean);
appModule.getEjbModules().add(new EjbModule(ejbJar));
} else if (obj instanceof Application) {
application = (Application) obj;
} else if (obj instanceof Connector) {
final Connector connector = (Connector) obj;
appModule.getConnectorModules().add(new ConnectorModule(connector));
} else if (obj instanceof Persistence) {
final Persistence persistence = (Persistence) obj;
appModule.getPersistenceModules().add(new PersistenceModule("", persistence));
} else if (obj instanceof PersistenceUnit) {
final PersistenceUnit unit = (PersistenceUnit) obj;
appModule.getPersistenceModules().add(new PersistenceModule("", new Persistence(unit)));
} else if (obj instanceof Beans) {
final Beans beans = (Beans) obj;
final EjbModule ejbModule = new EjbModule(new EjbJar());
ejbModule.setBeans(beans);
appModule.getEjbModules().add(ejbModule);
}
}
// Application is final in AppModule, which is fine, so we'll create a new one and move everything
if (application != null) {
final AppModule newModule = new AppModule(appModule.getClassLoader(), appModule.getModuleId(), application, false);
newModule.getClientModules().addAll(appModule.getClientModules());
newModule.getPersistenceModules().addAll(appModule.getPersistenceModules());
newModule.getEjbModules().addAll(appModule.getEjbModules());
newModule.getConnectorModules().addAll(appModule.getConnectorModules());
appModule = newModule;
}
m = appModule;
}
return m;
}
// TODO, report some information
private EJBException specifiedModulesNotFound() {
return new NoSuchModuleException("some modules not matched on classpath");
}
private InvalidModulesPropertyException invalidModulesValue(Object value) {
String[] spec = {"java.lang.String", "java.lang.String[]", "java.io.File", "java.io.File[]"};
// TODO
// String[] vendor = {"java.lang.Class","java.lang.Class[]", "java.net.URL", "java.io.URL[]"};
String type = (value == null) ? null : value.getClass().getName();
return new InvalidModulesPropertyException(String.format("Invalid '%s' value '%s'. Valid values are: %s", EJBContainer.MODULES, type, Join.join(", ", spec)));
}
private static boolean isOtherProvider(Map<?, ?> properties) {
Object provider = properties.get(EJBContainer.PROVIDER);
if (provider != null && !provider.equals(OpenEjbContainer.class) && !provider.equals(OpenEjbContainer.class.getName())) {
return true;
}
return false;
}
private boolean match(String s, File file) {
String s2 = file.getName();
boolean matches;
if (file.isDirectory()) {
matches = s2.equals(s) || s2.equals(s + ".jar");
} else {
matches = s2.equals(s + ".jar");
}
if (!matches) {
//TODO look for ejb-jar.xml with matching module name
}
return matches;
}
}
private class GlobalContext extends ContextFlyweight {
private final Context globalJndiContext;
public GlobalContext(Context globalJndiContext) {
this.globalJndiContext = globalJndiContext;
}
@Override
protected Context getContext() throws NamingException {
return globalJndiContext;
}
@Override
protected Name getName(Name name) throws NamingException {
String first = name.get(0);
if (!first.startsWith("java:")) throw new NameNotFoundException("Name must be in java: namespace");
first = first.substring("java:".length());
name = name.getSuffix(1);
return name.add(0, first);
}
@Override
protected String getName(String name) throws NamingException {
if ("inject".equals(name)) return name;
if (!name.startsWith("java:")) throw new NameNotFoundException("Name must be in java: namespace");
return name.substring("java:".length());
}
@Override
public void bind(Name name, Object obj) throws NamingException {
if (name.size() == 1 && "inject".equals(name.get(0))) inject(obj);
else super.bind(name, obj);
}
@Override
public void bind(String name, Object obj) throws NamingException {
if (name != null && "inject".equals(name)) inject(obj);
else super.bind(name, obj);
}
}
public static class InitializationException extends EJBException {
public InitializationException(String s) {
super(s);
}
public InitializationException(Exception cause) {
super(cause);
}
}
public static class InvalidModulesPropertyException extends InitializationException {
public InvalidModulesPropertyException(String s) {
super(s);
}
}
public static class NoSuchModuleException extends InitializationException {
public NoSuchModuleException(String s) {
super(s);
}
}
public static class NoModulesFoundException extends InitializationException {
public NoModulesFoundException(String s) {
super(s);
}
}
public static class ConfigureApplicationException extends InitializationException {
public ConfigureApplicationException(Exception cause) {
super(cause);
}
}
public static class AssembleApplicationException extends InitializationException {
public AssembleApplicationException(Exception cause) {
super(cause);
}
}
public static class InvalidApplicationException extends InitializationException {
public InvalidApplicationException(Exception cause) {
super(cause);
}
}
}