| /* |
| * 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.api.LocalClient; |
| import org.apache.openejb.assembler.classic.AppInfo; |
| import org.apache.openejb.assembler.classic.Assembler; |
| import org.apache.openejb.config.*; |
| import org.apache.openejb.core.Operation; |
| import org.apache.openejb.core.ThreadContext; |
| import org.apache.openejb.jee.*; |
| 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.*; |
| import org.apache.webbeans.config.WebBeansContext; |
| import org.apache.webbeans.inject.AbstractInjectable; |
| import org.apache.webbeans.inject.OWBInjector; |
| import org.apache.webbeans.web.lifecycle.test.MockHttpSession; |
| import org.apache.webbeans.web.lifecycle.test.MockServletContext; |
| 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.servlet.ServletContext; |
| import javax.servlet.http.HttpSession; |
| import javax.validation.ValidationException; |
| import java.io.File; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.*; |
| import java.util.logging.LogManager; |
| |
| import static org.apache.openejb.cdi.ScopeHelper.startContexts; |
| import static org.apache.openejb.cdi.ScopeHelper.stopContexts; |
| |
| /** |
| * @version $Rev$ $Date$ |
| */ |
| public class OpenEjbContainer extends EJBContainer { |
| |
| static { |
| Core.warmup(); |
| |
| // if tomee embedded was ran we'll lost log otherwise |
| final String logManger = System.getProperty("java.util.logging.manager"); |
| if (logManger != null) { |
| try { |
| Thread.currentThread().getContextClassLoader().loadClass(logManger); |
| } catch (Exception ignored) { |
| final Field field; |
| try { |
| field = LogManager.class.getDeclaredField("manager"); |
| field.setAccessible(true); |
| field.set(null, new JuliLogStreamFactory.OpenEJBLogManager()); |
| } catch (Exception ignore) { |
| // ignore |
| } |
| } |
| } |
| } |
| |
| 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 ServiceManagerProxy serviceManager; |
| private Options options; |
| private OpenEjbContainer.GlobalContext globalJndiContext; |
| private WebBeansContext webBeanContext; |
| private ServletContext servletContext; |
| private HttpSession session; |
| |
| private OpenEjbContainer(Map<?, ?> map, AppContext appContext) { |
| webBeanContext = appContext.getWebBeansContext(); |
| globalJndiContext = new GlobalContext(appContext.getGlobalJndiContext()); |
| |
| final Properties properties = new Properties(); |
| properties.putAll(map); |
| |
| options = new Options(properties); |
| |
| startNetworkServices(); |
| |
| servletContext = new MockServletContext(); |
| session = new MockHttpSession(); |
| try { |
| startContexts(webBeanContext.getContextsService(), servletContext, session); |
| } catch (Exception e) { |
| logger.warning("can't start all CDI contexts", e); |
| } |
| } |
| |
| @Override |
| public void close() { |
| if (serviceManager != null) { |
| serviceManager.stop(); |
| } |
| try { |
| globalJndiContext.close(); |
| } catch (NamingException e) { |
| throw new IllegalStateException(e); |
| } |
| |
| final Assembler assembler = SystemInstance.get().getComponent(Assembler.class); |
| if (assembler != null) { |
| for (AppInfo info : assembler.getDeployedApplications()) { |
| try { |
| assembler.destroyApplication(info); |
| } catch (UndeployException e) { |
| logger.error(e.getMessage(), e); |
| } |
| } |
| } |
| |
| try { |
| stopContexts(webBeanContext.getContextsService(), servletContext, session); |
| } catch (Exception e) { |
| logger.warning("can't stop all CDI contexts", e); |
| } |
| |
| logger.info("Destroying OpenEJB container"); |
| 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) { // found the test class directly |
| final InjectionProcessor processor = new InjectionProcessor(object, context.getInjections(), context.getJndiContext()); |
| cdiInjections(context, object); |
| try { |
| return (T) processor.createInstance(); |
| } catch (OpenEJBException e) { |
| throw new InjectionException(clazz.getName(), e); |
| } |
| } else if (!isAnnotatedLocalClient(clazz)) { // nothing to do |
| throw new NoInjectionMetaDataException(clazz.getName()); |
| } |
| |
| // the test class was not found in beans (OpenEJB ran from parent) but was annotated @LocalClient |
| try { |
| final InjectionProcessor<?> processor = ClientInjections.clientInjector(object); |
| cdiInjections(null, object); |
| return (T) processor.createInstance(); |
| } catch (OpenEJBException e) { |
| throw new NoInjectionMetaDataException("Injection failed", e); |
| } |
| } |
| |
| private <T> void cdiInjections(final BeanContext context, final T object) { |
| ThreadContext oldContext = null; |
| if (context != null) { |
| final ThreadContext callContext = new ThreadContext(context, null, Operation.INJECTION); |
| oldContext = ThreadContext.enter(callContext); |
| } |
| try { |
| AbstractInjectable.instanceUnderInjection.set(object); |
| try { |
| OWBInjector.inject(webBeanContext.getBeanManagerImpl(), object, null); |
| } finally { |
| AbstractInjectable.instanceUnderInjection.remove(); |
| } |
| } catch (Throwable t) { |
| logger.warning("an error occured while injecting the class '" + object.getClass().getName() + "': " + t.getMessage()); |
| } finally { |
| if (context != null) { |
| ThreadContext.exit(oldContext); |
| } |
| } |
| } |
| |
| private boolean isAnnotatedLocalClient(final Class<?> clazz) { |
| Class<?> current = clazz; |
| while (current != null && current != Object.class) { |
| if (current.getAnnotation(LocalClient.class) != null) { |
| return true; |
| } |
| current = current.getSuperclass(); |
| } |
| return false; |
| } |
| |
| private 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(final String s) { |
| this(s, null); |
| } |
| |
| public NoInjectionMetaDataException(final String s, final Exception e) { |
| super(String.format("%s : Annotate the class with @%s so it can be discovered in the application scanning process", s, javax.annotation.ManagedBean.class.getName()), e); |
| } |
| } |
| |
| 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); |
| |
| DeploymentLoader.reloadAltDD(); // otherwise hard to use multiple altdd with several start/stop in the same JVM |
| |
| 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) { |
| // no-op |
| } |
| } |
| } |
| } |
| |
| private boolean isValid(String caller) { |
| try { |
| final ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| |
| final Class<?> clazz = loader.loadClass(caller); |
| |
| return !clazz.isEnum() && !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()); |
| } 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(); |
| ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
| |
| 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; |
| |
| { |
| Application application = null; |
| AppModule appModule = new AppModule(this.getClass().getClassLoader(), appId); |
| |
| { |
| if (modules instanceof EjbJar) { |
| |
| final EjbJar ejbJar = (EjbJar) modules; |
| appModule.getEjbModules().add(new EjbModule(ejbJar)); |
| |
| } else if (modules instanceof EnterpriseBean) { |
| |
| final EnterpriseBean bean = (EnterpriseBean) modules; |
| final EjbJar ejbJar = new EjbJar(); |
| ejbJar.addEnterpriseBean(bean); |
| appModule.getEjbModules().add(new EjbModule(ejbJar)); |
| |
| } else if (modules instanceof Application) { |
| |
| application = (Application) modules; |
| |
| } else if (modules instanceof Connector) { |
| |
| final Connector connector = (Connector) modules; |
| appModule.getConnectorModules().add(new ConnectorModule(connector)); |
| |
| } else if (modules instanceof Persistence) { |
| |
| final Persistence persistence = (Persistence) modules; |
| appModule.addPersistenceModule(new PersistenceModule(appModule, "", persistence)); |
| |
| } else if (modules instanceof PersistenceUnit) { |
| |
| final PersistenceUnit unit = (PersistenceUnit) modules; |
| appModule.addPersistenceModule(new PersistenceModule(appModule, "", new Persistence(unit))); |
| |
| } else if (modules instanceof Beans) { |
| |
| final Beans beans = (Beans) modules; |
| 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.addPersistenceModules(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) { |
| final Object provider = properties.get(EJBContainer.PROVIDER); |
| return provider != null && !provider.equals(OpenEjbContainer.class) && !provider.equals(OpenEjbContainer.class.getName()) |
| && !"openejb".equals(provider); |
| } |
| |
| private boolean match(String s, File file) { |
| final String s2 = file.getName(); |
| final String s3 = file.getAbsolutePath(); |
| boolean matches; |
| if (file.isDirectory()) { |
| matches = s2.equals(s) || s2.equals(s + ".jar") || s3.equals(s); |
| } 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); |
| } |
| |
| @Override |
| public void unbind(Name name) throws NamingException { |
| if (!(name.size() == 1 && "inject".equals(name.get(0)))) { |
| super.unbind(name); |
| } |
| } |
| |
| @Override |
| public void unbind(String name) throws NamingException { |
| if (!(name != null && "inject".equals(name))) { |
| super.unbind(name); |
| } |
| } |
| } |
| |
| |
| 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); |
| } |
| } |
| } |