001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.ejb.manager;
031:
032: import java.util.*;
033: import java.util.logging.*;
034: import javax.jms.*;
035:
036: import com.caucho.amber.manager.AmberContainer;
037: import com.caucho.amber.manager.AmberPersistenceUnit;
038: import com.caucho.config.*;
039: import com.caucho.ejb.AbstractServer;
040: import com.caucho.ejb.cfg.EjbConfigManager;
041: import com.caucho.ejb.cfg.EjbRootConfig;
042: import com.caucho.ejb.entity.EntityCache;
043: import com.caucho.ejb.protocol.EjbProtocolManager;
044: import com.caucho.ejb.xa.EjbTransactionManager;
045: import com.caucho.java.WorkDir;
046: import com.caucho.loader.*;
047: import com.caucho.loader.enhancer.ScanListener;
048: import com.caucho.util.*;
049: import com.caucho.vfs.*;
050:
051: /**
052: * Environment-based container.
053: */
054: public class EjbContainer implements ScanListener, EnvironmentListener {
055: private static final L10N L = new L10N(EjbContainer.class);
056: private static final Logger log = Logger
057: .getLogger(EjbContainer.class.getName());
058:
059: private static final EnvironmentLocal<EjbContainer> _localContainer = new EnvironmentLocal<EjbContainer>();
060:
061: private final EnvironmentClassLoader _classLoader;
062: private final ClassLoader _tempClassLoader;
063:
064: private final EjbContainer _parentContainer;
065:
066: private final EjbConfigManager _configManager;
067: private final EjbTransactionManager _transactionManager;
068: private final EjbProtocolManager _protocolManager;
069: private final EntityCache _entityCache;
070:
071: private AmberPersistenceUnit _ejbPersistenceUnit;
072:
073: private HashSet<String> _ejbUrls = new HashSet<String>();
074:
075: //
076: // configuration
077: //
078:
079: private boolean _isAutoCompile = true;
080: private Path _workDir;
081:
082: private ConnectionFactory _jmsConnectionFactory;
083: private int _messageConsumerMax = 5;
084:
085: //
086: // active servers
087: //
088:
089: private ArrayList<AbstractServer> _serverList = new ArrayList<AbstractServer>();
090:
091: private EjbContainer(ClassLoader loader) {
092: _parentContainer = _localContainer.get(loader);
093:
094: _classLoader = Environment.getEnvironmentClassLoader(loader);
095:
096: _tempClassLoader = _classLoader.getNewTempClassLoader();
097:
098: _localContainer.set(this , _classLoader);
099:
100: if (_parentContainer != null)
101: copyContainerDefaults(_parentContainer);
102:
103: _transactionManager = new EjbTransactionManager(this );
104:
105: // _ejbAdmin = new EJBAdmin(this);
106:
107: _protocolManager = new EjbProtocolManager(this );
108:
109: _configManager = new EjbConfigManager(this );
110:
111: _entityCache = new EntityCache();
112:
113: _workDir = WorkDir.getLocalWorkDir().lookup("ejb");
114:
115: _classLoader.addScanListener(this );
116:
117: Environment.addEnvironmentListener(this );
118: }
119:
120: /**
121: * Returns the local container.
122: */
123: public static EjbContainer create() {
124: return create(Thread.currentThread().getContextClassLoader());
125: }
126:
127: /**
128: * Returns the local container.
129: */
130: public static EjbContainer create(ClassLoader loader) {
131: synchronized (_localContainer) {
132: EjbContainer container = _localContainer.getLevel(loader);
133:
134: if (container == null) {
135: container = new EjbContainer(loader);
136:
137: _localContainer.set(container, loader);
138: }
139:
140: return container;
141: }
142: }
143:
144: /**
145: * Returns the local container.
146: */
147: public static EjbContainer getCurrent() {
148: return getCurrent(Thread.currentThread()
149: .getContextClassLoader());
150: }
151:
152: /**
153: * Returns the current environment container.
154: */
155: public static EjbContainer getCurrent(ClassLoader loader) {
156: synchronized (_localContainer) {
157: return _localContainer.get(loader);
158: }
159: }
160:
161: /**
162: * Returns the parent loader
163: */
164: public EnvironmentClassLoader getClassLoader() {
165: return _classLoader;
166: }
167:
168: /**
169: * Returns the introspection class loader
170: */
171: public ClassLoader getIntrospectionClassLoader() {
172: return _tempClassLoader;
173: }
174:
175: /**
176: * Returns the configuration manager.
177: */
178: public EjbConfigManager getConfigManager() {
179: return _configManager;
180: }
181:
182: public EjbContainer getParent() {
183: return _parentContainer;
184: }
185:
186: /**
187: * Returns the protocol manager.
188: */
189: public EjbProtocolManager getProtocolManager() {
190: return _protocolManager;
191: }
192:
193: /**
194: * Returns the transaction manager.
195: */
196: public EjbTransactionManager getTransactionManager() {
197: return _transactionManager;
198: }
199:
200: /**
201: * Returns the entity cache.
202: */
203: public EntityCache getEntityCache() {
204: return _entityCache;
205: }
206:
207: /**
208: * Returns the amber persistence unit for ejb.
209: */
210: public AmberPersistenceUnit createEjbPersistenceUnit() {
211: if (_ejbPersistenceUnit == null) {
212: try {
213: AmberContainer amber = AmberContainer
214: .create(_classLoader);
215:
216: _ejbPersistenceUnit = amber
217: .createPersistenceUnit("resin-ejb");
218: _ejbPersistenceUnit.setBytecodeGenerator(false);
219: ClassLoader loader = SimpleLoader.create(getWorkDir());
220: _ejbPersistenceUnit.setEnhancedLoader(loader);
221: _ejbPersistenceUnit.initLoaders();
222: // _ejbPersistenceUnit.setTableCacheTimeout(_entityCacheTimeout);
223: } catch (RuntimeException e) {
224: throw e;
225: } catch (Exception e) {
226: throw ConfigException.create(e);
227: }
228: }
229:
230: return _ejbPersistenceUnit;
231: }
232:
233: //
234: // configuration
235: //
236:
237: /**
238: * true if beans should be auto-compiled
239: */
240: public void setAutoCompile(boolean isAutoCompile) {
241: _isAutoCompile = isAutoCompile;
242: }
243:
244: /**
245: * true if beans should be auto-compiled
246: */
247: public boolean isAutoCompile() {
248: return _isAutoCompile;
249: }
250:
251: /**
252: * The work directory for EJB-generated files
253: */
254: public void setWorkDir(Path workDir) {
255: _workDir = workDir;
256: }
257:
258: /**
259: * The work directory for EJB-generated files
260: */
261: public Path getWorkDir() {
262: return _workDir;
263: }
264:
265: /**
266: * The JMS connection factory for the container.
267: */
268: public void setJmsConnectionFactory(ConnectionFactory factory) {
269: _jmsConnectionFactory = factory;
270: }
271:
272: /**
273: * Sets the JMS connection factory for the container.
274: */
275: public ConnectionFactory getJmsConnectionFactory() {
276: return _jmsConnectionFactory;
277: }
278:
279: /**
280: * Sets the consumer maximum for the container.
281: */
282: public void setMessageConsumerMax(int consumerMax) {
283: _messageConsumerMax = consumerMax;
284: }
285:
286: /**
287: * The consumer maximum for the container.
288: */
289: public int getMessageConsumerMax() {
290: return _messageConsumerMax;
291: }
292:
293: /**
294: * Copy defaults from the parent container when first created.
295: */
296: private void copyContainerDefaults(EjbContainer parent) {
297: _isAutoCompile = parent._isAutoCompile;
298: _jmsConnectionFactory = parent._jmsConnectionFactory;
299: _messageConsumerMax = parent._messageConsumerMax;
300: }
301:
302: //
303: // AbstractServer management
304: //
305:
306: /**
307: * Adds a server.
308: */
309: public void addServer(AbstractServer server) {
310: _serverList.add(server);
311:
312: getProtocolManager().addServer(server);
313: }
314:
315: /**
316: * Returns the server specified by the ejbName, or null if not found.
317: */
318: public AbstractServer getServer(String ejbName) {
319: for (AbstractServer server : _serverList) {
320: if (server.getEJBName().equals(ejbName)) {
321: return server;
322: }
323: }
324:
325: return null;
326: }
327:
328: /**
329: * Returns the server specified by the path and ejbName,
330: * or null if not found.
331: */
332: public AbstractServer getServer(Path path, String ejbName) {
333: String mappedName = path.getFullPath() + "#" + ejbName;
334:
335: for (AbstractServer server : _serverList) {
336: if (mappedName.equals(server.getId())) {
337: return server;
338: }
339: }
340:
341: return null;
342: }
343:
344: //
345: // Deployment information
346: //
347:
348: /**
349: * Returns the information for a client remote configuration, e.g.
350: * the <ejb-ref> needed for the client to properly connect.
351: *
352: * Only needed for the TCK.
353: */
354: public String getClientRemoteConfig() {
355: StringBuilder sb = new StringBuilder();
356:
357: sb.append("<!-- test references -->");
358:
359: for (AbstractServer server : _serverList) {
360: server.addClientRemoteConfig(sb);
361: }
362:
363: return sb.toString();
364: }
365:
366: //
367: // ScanListener
368: //
369:
370: /**
371: * Adds a root URL
372: */
373: public void addRoot(Path root) {
374: if (root.getURL().endsWith(".jar"))
375: root = JarPath.create(root);
376:
377: // XXX: ejb/0fbn
378: Path ejbJar = root.lookup("META-INF/ejb-jar.xml");
379: if (ejbJar.canRead())
380: getConfigManager().addEjbPath(ejbJar);
381:
382: _ejbUrls.add(root.getURL());
383: }
384:
385: /**
386: * Returns true if the root is a valid scannable root.
387: */
388: public boolean isRootScannable(Path root) {
389: if (_ejbUrls.contains(root.getURL())) {
390: } else if (!root.lookup("META-INF/ejb-jar.xml").canRead())
391: return false;
392:
393: EjbRootConfig context = _configManager.createRootConfig(root);
394:
395: if (context.isScanComplete())
396: return false;
397: else {
398: context.setScanComplete(true);
399: return true;
400: }
401: }
402:
403: public boolean isScanMatch(CharBuffer annotationName) {
404: if (annotationName.matches("javax.ejb.Stateless"))
405: return true;
406: else if (annotationName.matches("javax.ejb.Stateful"))
407: return true;
408: else if (annotationName.matches("javax.ejb.MessageDriven"))
409: return true;
410: else
411: return false;
412: }
413:
414: /**
415: * Callback to note the class matches
416: */
417: public void classMatchEvent(EnvironmentClassLoader loader,
418: Path root, String className) {
419: EjbRootConfig config = _configManager.createRootConfig(root);
420: config.addClassName(className);
421: }
422:
423: //
424: // lifecycle methods
425: //
426:
427: public void init() {
428: }
429:
430: private void config() {
431: _configManager.start();
432: }
433:
434: public void start() throws ConfigException {
435: try {
436: AmberContainer.create().start();
437:
438: Thread thread = Thread.currentThread();
439: ClassLoader oldLoader = thread.getContextClassLoader();
440:
441: for (AbstractServer server : _serverList) {
442: try {
443: thread.setContextClassLoader(server
444: .getClassLoader());
445:
446: server.start();
447: } finally {
448: thread.setContextClassLoader(oldLoader);
449: }
450: }
451:
452: AmberContainer.create().start();
453: } catch (RuntimeException e) {
454: throw e;
455: } catch (Exception e) {
456: throw ConfigException.create(e);
457: }
458: }
459:
460: /**
461: * Closes the container.
462: */
463: public void destroy() {
464: /*
465: if (! _lifecycle.toDestroy())
466: return;
467: */
468:
469: try {
470: ArrayList<AbstractServer> servers;
471: servers = new ArrayList<AbstractServer>(_serverList);
472:
473: _serverList.clear();
474:
475: // only purpose of the sort is to make the qa order consistent
476: Collections.sort(servers, new ServerCmp());
477:
478: for (AbstractServer server : servers) {
479: try {
480: getProtocolManager().removeServer(server);
481: } catch (Throwable e) {
482: log.log(Level.WARNING, e.toString(), e);
483: }
484: }
485:
486: for (AbstractServer server : servers) {
487: try {
488: server.destroy();
489: } catch (Throwable e) {
490: log.log(Level.WARNING, e.toString(), e);
491: }
492: }
493: } catch (Throwable e) {
494: log.log(Level.WARNING, e.toString(), e);
495: }
496: }
497:
498: /**
499: * Handles the case where the environment is configuring
500: */
501: public void environmentConfig(EnvironmentClassLoader loader) {
502: config();
503: }
504:
505: /**
506: * Handles the case where the environment is starting (after init).
507: */
508: public void environmentStart(EnvironmentClassLoader loader) {
509: start();
510: }
511:
512: /**
513: * Handles the case where the environment is stopping
514: */
515: public void environmentStop(EnvironmentClassLoader loader) {
516: destroy();
517: }
518:
519: public String toString() {
520: return getClass().getSimpleName() + "[" + _classLoader + "]";
521: }
522:
523: /**
524: * Sorts the servers so they can be destroyed in a consistent order.
525: * (To make QA sane.)
526: */
527: static class ServerCmp implements Comparator<AbstractServer> {
528: public int compare(AbstractServer a, AbstractServer b) {
529: return a.getEJBName().compareTo(b.getEJBName());
530: }
531: }
532: }
|