/* * Copyright (c) 1998-2011 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.ejb.server; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.ejb.DependsOn; import javax.ejb.FinderException; import javax.ejb.Timer; import javax.ejb.TimerService; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Bean; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.UserTransaction; import com.caucho.config.ConfigException; import com.caucho.config.Configurable; import com.caucho.config.LineConfigException; import com.caucho.config.inject.CreationalContextImpl; import com.caucho.config.inject.InjectManager; import com.caucho.config.inject.OwnerCreationalContext; import com.caucho.config.program.ConfigProgram; import com.caucho.config.types.ResourceGroupConfig; import com.caucho.ejb.cfg.AroundInvokeConfig; import com.caucho.ejb.manager.EjbManager; import com.caucho.ejb.manager.EjbModule; import com.caucho.inject.RequestContext; import com.caucho.lifecycle.Lifecycle; import com.caucho.loader.DynamicClassLoader; import com.caucho.loader.EnvironmentBean; import com.caucho.loader.EnvironmentClassLoader; import com.caucho.transaction.UserTransactionProxy; import com.caucho.util.L10N; /** * Base server for a single home/object bean pair. */ abstract public class AbstractEjbBeanManager<X> implements EnvironmentBean { private final static Logger log = Logger.getLogger(AbstractEjbBeanManager.class.getName()); private static final L10N L = new L10N(AbstractEjbBeanManager.class); protected final EjbManager _ejbManager; private final EjbModule _ejbModule; private String _moduleName; protected final UserTransaction _ut = UserTransactionProxy.getInstance(); protected String _filename; protected int _line; protected String _location; // The original bean implementation class protected Class<X> _ejbClass; // introspected bean information private AnnotatedType<X> _rawAnnotatedType; private AnnotatedType<X> _annotatedType; private Bean<X> _bean; private String _id; private final String _ejbName; // name for IIOP, Hessian, JNDI protected String _mappedName; private ArrayList<Class<?>> _remoteApiList = new ArrayList<Class<?>>(); private Context _jndiEnv; // server-specific classloader private EnvironmentClassLoader _loader; private ConfigProgram _serverProgram; private ArrayList<ResourceGroupConfig> _resourceList; // injection/postconstruct from Java Injection private EjbInjectionTarget<X> _producer; private boolean _isContainerTransaction = true; private long _transactionTimeout; private final Lifecycle _lifecycle = new Lifecycle(); private InjectManager _moduleInjectManager; private InjectManager _ejbInjectManager; /** * Creates a new server container * * @param manager * the owning server container */ public AbstractEjbBeanManager(EjbManager ejbManager, String ejbName, String moduleName, AnnotatedType<X> rawAnnotatedType, AnnotatedType<X> annotatedType) { _ejbName = ejbName; _rawAnnotatedType = rawAnnotatedType; _annotatedType = annotatedType; _ejbManager = ejbManager; _ejbModule = EjbModule.getCurrent(); if (moduleName == null) moduleName = _ejbModule.getModuleName(); _moduleName = moduleName; if (_ejbModule == null) throw new IllegalStateException(L.l("EjbModule is not currently defined.")); _loader = EnvironmentClassLoader.create(ejbManager.getClassLoader()); // XXX: 4.0.7 this is complicated by decorator vs context injection _loader.setAttribute("caucho.inject", false); _loader.setAttribute("ejb.manager", false); _loader.setId("ejb:" + ejbName); _producer = createInjectionTarget(); _moduleInjectManager = InjectManager.create(); _ejbInjectManager = InjectManager.create(_loader); _ejbInjectManager.setJndiClassLoader(_moduleInjectManager.getClassLoader()); } protected EjbInjectionTarget<X> createInjectionTarget() { return new EjbInjectionTarget<X>(this, getAnnotatedType()); } /** * Returns the id, module-path#ejb-name. */ public String getId() { return _id; } public InjectManager getModuleInjectManager() { return _moduleInjectManager; } public InjectManager getInjectManager() { return _ejbInjectManager; } /** * Sets the id, module-path#ejb-name. */ public void setId(String id) { _id = id; int p = id.lastIndexOf('/'); if (p > 0) _loader.setId(getType() + id.substring(p + 1)); else _loader.setId(getType() + id); } public void setConfigLocation(String filename, int line) { _filename = filename; _line = line; } public void setLocation(String location) { _location = location; } protected String getType() { return "ejb:"; } public Bean<X> getDeployBean() { return _bean; } public void setAroundInvoke(AroundInvokeConfig aroundInvoke) { } /** * Returns the ejb's name */ public String getEJBName() { return _ejbName; } /** * Returns the module that defined this ejb. */ public String getModuleName() { // return _ejbModule.getModuleName(); return _moduleName; } /** * Sets the mapped name, default is to use the EJBName. This is the name for * both JNDI and the protocols such as IIOP and Hessian. */ public void setMappedName(String mappedName) { if (mappedName == null) { _mappedName = null; return; } while (mappedName.startsWith("/")) mappedName = mappedName.substring(1); while (mappedName.endsWith("/")) mappedName = mappedName.substring(0, mappedName.length() - 1); _mappedName = mappedName; } /** * Returns the mapped name. */ public String getMappedName() { return _mappedName == null ? getEJBName() : _mappedName; } /** * The name to use for remoting protocols, such as IIOP and Hessian. */ public String getProtocolId() { return "/" + getMappedName(); } /** * The name to use for remoting protocols, such as IIOP and Hessian. */ public String getProtocolId(Class<?> cl) { if (cl == null) return getProtocolId(); // XXX TCK: // ejb30/bb/session/stateless/callback/defaultinterceptor/descriptor/defaultInterceptorsForCallbackBean1 if (cl.getName().startsWith("java.")) return getProtocolId(); // Adds the suffix "#com_sun_ts_tests_ejb30_common_sessioncontext_Three1IF"; String url = getProtocolId() + "#" + cl.getName().replace(".", "_"); return url; } public AnnotatedType<X> getRawAnnotatedType() { return _rawAnnotatedType; } public AnnotatedType<X> getAnnotatedType() { return _annotatedType; } /** * Sets the ejb class */ public void setEjbClass(Class<X> cl) { _ejbClass = cl; } /** * Sets the ejb class */ public Class<X> getEjbClass() { return _annotatedType.getJavaClass(); } /** * Sets the remote object list. */ public void setRemoteApiList(ArrayList<Class<?>> list) { _remoteApiList = new ArrayList<Class<?>>(list); } /** * Returns the remote object list. */ public ArrayList<Class<?>> getRemoteApiList() { return _remoteApiList; } /** * Returns true if there is any remote object. */ public boolean hasRemoteObject() { return _remoteApiList.size() > 0; } public ArrayList<AnnotatedType<? super X>> getLocalApi() { throw new UnsupportedOperationException(getClass().getName()); } public ArrayList<AnnotatedType<? super X>> getRemoteApi() { throw new UnsupportedOperationException(getClass().getName()); } public AnnotatedType<X> getLocalBean() { return null; } /** * Returns the encoded id. */ public String encodeId(Object primaryKey) { return String.valueOf(primaryKey); } /** * @param bindList * @return */ public boolean isDependValid(ArrayList<AbstractEjbBeanManager<?>> bindList) { DependsOn dependsOn = getAnnotatedType().getAnnotation(DependsOn.class); if (dependsOn == null) return true; for (String dep : dependsOn.value()) { if (! isStarted(dep, bindList)) return false; } return true; } private boolean isStarted(String dep, ArrayList<AbstractEjbBeanManager<?>> bindList) { for (AbstractEjbBeanManager<?> manager : bindList) { if (dep.equals(manager.getEJBName())) return true; } return false; } /** * Looks up the JNDI object. */ public Object lookup(String jndiName) { try { if (_jndiEnv == null) _jndiEnv = (Context) new InitialContext();//.lookup("java:comp/env"); if (jndiName == null) throw new IllegalArgumentException(); if (jndiName.indexOf(':') < 0) jndiName = "java:comp/env/" + jndiName; // XXX: not tested return _jndiEnv.lookup(jndiName); } catch (NamingException e) { throw new IllegalArgumentException(e); } } public UserTransaction getUserTransaction() { return _ut; } /** * Returns the owning container. */ public EjbManager getEjbContainer() { return _ejbManager; } /** * Sets the server program. */ public void setServerProgram(ConfigProgram serverProgram) { _serverProgram = serverProgram; } /** * Sets the server program. */ public ConfigProgram getServerProgram() { return _serverProgram; } /** * Sets the transaction timeout. */ public void setTransactionTimeout(long timeout) { _transactionTimeout = timeout; } /** * Gets the transaction timeout. */ public long getTransactionTimeout() { return _transactionTimeout; } @Configurable public void setBusinessLocal(Class<?> local) { } /** * Returns the timer service. */ public TimerService getTimerService() { TimerService service = _producer.getTimerService(); if (service != null) return service; // ejb/0fj0 throw new UnsupportedOperationException(L.l("'{0}' does not support a timer service because it does not have a @Timeout method", this)); } /** * Invalidates caches. */ public void invalidateCache() { } /** * Gets the class loader */ public DynamicClassLoader getClassLoader() { return _loader; } public void bind() { } /** * Returns the remote skeleton for the given API * * @param api * the bean's api to return a value for * @param protocol * the remote protocol */ abstract public <T> T getRemoteObject(Class<T> api, String protocol); /** * Returns the a new local stub for the given API * * @param api * the bean's api to return a value for */ abstract public <T> T getLocalProxy(Class<T> api); /** * Returns the local jndi proxy for the given API * * @param api * the bean's api to return a value for */ abstract public <T> Object getLocalJndiProxy(Class<T> api); public AbstractContext<X> getContext() { return null; } public AbstractContext<?> getContext(Object key) throws FinderException { return getContext(key, true); } /** * Returns the context with the given key */ abstract public AbstractContext<?> getContext(Object key, boolean forceLoad) throws FinderException; public void timeout(Timer timer) { /* throw new UnsupportedOperationException(L.l("EJB '{0}' does not support a timeout, because it does not have a @Timeout method", this)); */ try { RequestContext.begin(); // XXX: needs reintegration OwnerCreationalContext env = new OwnerCreationalContext(_producer.getBean()); Object instance = newInstance(env); Method method = _producer.getTimeoutMethod(); if (method.getParameterTypes().length == 0) method.invoke(instance); else method.invoke(instance, timer); destroy(instance, env); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } finally { RequestContext.end(); } } public void init() throws Exception { _loader.init(); // _loader.setId("EnvironmentLoader[ejb:" + getId() + "]"); } public X newInstance(CreationalContextImpl<X> env) { return _producer.newInstance(env); } public void destroy(Object instance, CreationalContextImpl<?> env) { /* if (instance != null) _producer.destroyInstance((X) instance); */ } /** * Initialize an instance */ public void destroyInstance(X instance) { _producer.destroyInstance(instance); } public boolean start() throws Exception { if (! _lifecycle.toActive()) return false; Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { thread.setContextClassLoader(_loader); _loader.start(); if (_serverProgram != null) _serverProgram.configure(this); bindInjection(); postStart(); log.config(this + " active"); } finally { thread.setContextClassLoader(oldLoader); } return true; } protected void bindContext() { for (ResourceGroupConfig resource : _resourceList) { resource.deploy(); } } protected void registerInjection() { _producer.setEnvLoader(_loader); _producer.registerInjection(); } protected void bindInjection() { _producer.setEnvLoader(_loader); _producer.bindInjection(); } protected void postStart() { } /** * Returns true if container transaction is used. */ public boolean isContainerTransaction() { return _isContainerTransaction; } /** * Sets true if container transaction is used. */ public void setContainerTransaction(boolean isContainerTransaction) { _isContainerTransaction = isContainerTransaction; } public void setResourceList(ArrayList<ResourceGroupConfig> resourceList) { _resourceList = resourceList; } /** * Returns true if the server is dead. */ public boolean isDead() { return ! _lifecycle.isActive(); } /** * Cleans up the server on shutdown */ public void destroy() { _lifecycle.toDestroy(); } public ConfigException error(String msg) { if (_filename != null) throw new LineConfigException(_filename, _line, msg); else throw new ConfigException(msg); } public String toString() { if (getMappedName() != null) return (getClass().getSimpleName() + "[" + getEJBName() + "," + getMappedName() + "]"); else return getClass().getSimpleName() + "[" + getEJBName() + "]"; } }