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.loader;
031:
032: import com.caucho.jca.UserTransactionProxy;
033: import com.caucho.jmx.Jmx;
034: import com.caucho.lifecycle.Lifecycle;
035: import com.caucho.log.EnvironmentStream;
036: import com.caucho.loader.enhancer.ScanManager;
037: import com.caucho.loader.enhancer.ScanListener;
038: import com.caucho.management.j2ee.J2EEManagedObject;
039: import com.caucho.management.j2ee.JTAResource;
040: import com.caucho.management.server.ClassLoaderMXBean;
041: import com.caucho.naming.Jndi;
042: import com.caucho.security.PolicyImpl;
043: import com.caucho.transaction.TransactionManagerImpl;
044: import com.caucho.util.ResinThreadPoolExecutor;
045: import com.caucho.vfs.Vfs;
046:
047: import javax.management.*;
048: import java.lang.management.*;
049: import javax.naming.*;
050: import java.net.URL;
051: import java.lang.reflect.Method;
052: import java.util.ArrayList;
053: import java.util.Hashtable;
054: import java.util.Properties;
055: import java.util.logging.*;
056:
057: /**
058: * Class loader which checks for changes in class files and automatically
059: * picks up new jars.
060: *
061: * <p>DynamicClassLoaders can be chained creating one virtual class loader.
062: * From the perspective of the JDK, it's all one classloader. Internally,
063: * the class loader chain searches like a classpath.
064: */
065: public class EnvironmentClassLoader extends DynamicClassLoader {
066: private static Logger _log;
067:
068: private static boolean _isStaticInit;
069:
070: // listeners invoked at the start of any child environment
071: private static EnvironmentLocal<ArrayList<EnvironmentListener>> _childListeners = new EnvironmentLocal<ArrayList<EnvironmentListener>>();
072:
073: // listeners invoked when a Loader is added
074: private static EnvironmentLocal<ArrayList<AddLoaderListener>> _addLoaderListeners = new EnvironmentLocal<ArrayList<AddLoaderListener>>();
075:
076: // The owning bean
077: private EnvironmentBean _owner;
078:
079: // Class loader specific attributes
080: private Hashtable<String, Object> _attributes = new Hashtable<String, Object>(
081: 8);
082:
083: private ArrayList<ScanListener> _scanListeners;
084: private ArrayList<URL> _pendingScanUrls = new ArrayList<URL>();
085:
086: // Array of listeners
087: // XXX: this used to be a weak reference list, but that caused problems
088: // server/306i - can't be weak reference, instead create WeakStopListener
089: private ArrayList<EnvironmentListener> _listeners;
090: private WeakStopListener _stopListener;
091:
092: // The state of the environment
093: private volatile Lifecycle _lifecycle = new Lifecycle();
094:
095: private Throwable _configException;
096:
097: /**
098: * Creates a new environment class loader.
099: */
100: public EnvironmentClassLoader() {
101: this (Thread.currentThread().getContextClassLoader(), null);
102: }
103:
104: /**
105: * Creates a new environment class loader.
106: */
107: public EnvironmentClassLoader(String id) {
108: this (Thread.currentThread().getContextClassLoader(), id);
109: }
110:
111: /**
112: * Creates a new environment class loader.
113: */
114: public EnvironmentClassLoader(ClassLoader parent) {
115: this (parent, null);
116: }
117:
118: /**
119: * Creates a new environment class loader.
120: */
121: public EnvironmentClassLoader(ClassLoader parent, String id) {
122: super (parent);
123:
124: if (id != null)
125: setId(id);
126:
127: // initializeEnvironment();
128:
129: initListeners();
130: }
131:
132: /**
133: * Returns the environment's owner.
134: */
135: public EnvironmentBean getOwner() {
136: return _owner;
137: }
138:
139: /**
140: * Sets the environment's owner.
141: */
142: public void setOwner(EnvironmentBean owner) {
143: _owner = owner;
144: }
145:
146: /**
147: * Sets the config exception.
148: */
149: public void setConfigException(Throwable e) {
150: if (_configException == null)
151: _configException = e;
152: }
153:
154: /**
155: * Gets the config exception.
156: */
157: public Throwable getConfigException() {
158: return _configException;
159: }
160:
161: /**
162: * Returns true if the environment is active
163: */
164: public boolean isActive() {
165: return _lifecycle.isActive();
166: }
167:
168: /**
169: * Initialize the environment.
170: */
171: public void init() {
172: super .init();
173:
174: initEnvironment();
175: }
176:
177: protected void initEnvironment() {
178: initializeEnvironment();
179: }
180:
181: /**
182: * Returns the named attributes
183: */
184: public Object getAttribute(String name) {
185: if (_attributes != null)
186: return _attributes.get(name);
187: else
188: return null;
189: }
190:
191: /**
192: * Sets the named attributes
193: */
194: public Object setAttribute(String name, Object obj) {
195: if (obj == null) {
196: if (_attributes == null)
197: return null;
198: else
199: return _attributes.remove(name);
200: }
201:
202: if (_attributes == null)
203: _attributes = new Hashtable<String, Object>(8);
204:
205: return _attributes.put(name, obj);
206: }
207:
208: /**
209: * Removes the named attributes
210: */
211: public Object removeAttribute(String name) {
212: if (_attributes == null)
213: return null;
214: else
215: return _attributes.remove(name);
216: }
217:
218: /**
219: * Adds a listener to detect environment lifecycle changes.
220: */
221: public void addListener(EnvironmentListener listener) {
222: synchronized (this ) {
223: if (_listeners == null) {
224: _listeners = new ArrayList<EnvironmentListener>();
225:
226: initListeners();
227: }
228: }
229:
230: synchronized (_listeners) {
231: for (int i = _listeners.size() - 1; i >= 0; i--) {
232: EnvironmentListener oldListener = _listeners.get(i);
233:
234: if (listener == oldListener) {
235: return;
236: } else if (oldListener == null)
237: _listeners.remove(i);
238: }
239:
240: _listeners.add(listener);
241: }
242:
243: if (_lifecycle.isStarting()) {
244: listener.environmentConfig(this );
245: }
246:
247: if (_lifecycle.isActive()) {
248: listener.environmentStart(this );
249: }
250: }
251:
252: /**
253: * Adds self as a listener.
254: */
255: private void initListeners() {
256: ClassLoader parent = getParent();
257:
258: for (; parent != null; parent = parent.getParent()) {
259: if (parent instanceof EnvironmentClassLoader) {
260: EnvironmentClassLoader loader = (EnvironmentClassLoader) parent;
261:
262: if (_stopListener == null)
263: _stopListener = new WeakStopListener(this );
264:
265: loader.addListener(_stopListener);
266:
267: return;
268: }
269: }
270: }
271:
272: /**
273: * Adds a listener to detect environment lifecycle changes.
274: */
275: public void removeListener(EnvironmentListener listener) {
276: if (_listeners == null)
277: return;
278:
279: synchronized (_listeners) {
280: for (int i = _listeners.size() - 1; i >= 0; i--) {
281: EnvironmentListener oldListener = _listeners.get(i);
282:
283: if (listener == oldListener) {
284: _listeners.remove(i);
285: return;
286: } else if (oldListener == null)
287: _listeners.remove(i);
288: }
289: }
290: }
291:
292: /**
293: * Adds a child listener.
294: */
295: void addChildListener(EnvironmentListener listener) {
296: synchronized (_childListeners) {
297: ArrayList<EnvironmentListener> listeners = _childListeners
298: .getLevel(this );
299:
300: if (listeners == null) {
301: listeners = new ArrayList<EnvironmentListener>();
302:
303: _childListeners.set(listeners, this );
304: }
305:
306: listeners.add(listener);
307: }
308:
309: if (_lifecycle.isActive()) {
310: listener.environmentStart(this );
311: }
312: }
313:
314: /**
315: * Removes a child listener.
316: */
317: void removeChildListener(EnvironmentListener listener) {
318: synchronized (_childListeners) {
319: ArrayList<EnvironmentListener> listeners = _childListeners
320: .getLevel(this );
321:
322: if (listeners != null)
323: listeners.remove(listener);
324: }
325: }
326:
327: /**
328: * Returns the listeners.
329: */
330: protected ArrayList<EnvironmentListener> getEnvironmentListeners() {
331: ArrayList<EnvironmentListener> listeners;
332: listeners = new ArrayList<EnvironmentListener>();
333:
334: // add the descendent listeners
335: synchronized (_childListeners) {
336: ClassLoader loader;
337:
338: for (loader = this ; loader != null; loader = loader
339: .getParent()) {
340: if (loader instanceof EnvironmentClassLoader) {
341: ArrayList<EnvironmentListener> childListeners;
342: childListeners = _childListeners.getLevel(loader);
343:
344: if (childListeners != null)
345: listeners.addAll(childListeners);
346: }
347: }
348: }
349:
350: if (_listeners == null)
351: return listeners;
352:
353: synchronized (_listeners) {
354: for (int i = 0; i < _listeners.size(); i++) {
355: EnvironmentListener listener = _listeners.get(i);
356:
357: if (listener != null)
358: listeners.add(listener);
359: else {
360: _listeners.remove(i);
361: i--;
362: }
363: }
364: }
365:
366: return listeners;
367: }
368:
369: /**
370: * Adds a child listener.
371: */
372: public void addLoaderListener(AddLoaderListener listener) {
373: synchronized (_addLoaderListeners) {
374: ArrayList<AddLoaderListener> listeners = _addLoaderListeners
375: .getLevel(this );
376:
377: if (listeners == null) {
378: listeners = new ArrayList<AddLoaderListener>();
379:
380: _addLoaderListeners.set(listeners, this );
381: }
382:
383: listeners.add(listener);
384: }
385:
386: listener.addLoader(this );
387: }
388:
389: /**
390: * Returns the listeners.
391: */
392: protected ArrayList<AddLoaderListener> getLoaderListeners() {
393: ArrayList<AddLoaderListener> listeners;
394: listeners = new ArrayList<AddLoaderListener>();
395:
396: // add the descendent listeners
397: synchronized (_addLoaderListeners) {
398: ClassLoader loader;
399:
400: for (loader = this ; loader != null; loader = loader
401: .getParent()) {
402: if (loader instanceof EnvironmentClassLoader) {
403: ArrayList<AddLoaderListener> childListeners;
404: childListeners = _addLoaderListeners
405: .getLevel(loader);
406:
407: if (childListeners != null)
408: listeners.addAll(childListeners);
409: }
410: }
411: }
412:
413: return listeners;
414: }
415:
416: /**
417: * Adds a listener to detect class loader changes.
418: */
419: @Override
420: protected void sendAddLoaderEventImpl() {
421: ArrayList<AddLoaderListener> listeners = getLoaderListeners();
422:
423: for (int i = 0; listeners != null && i < listeners.size(); i++) {
424: listeners.get(i).addLoader(this );
425: }
426: }
427:
428: /**
429: * Adds the URL to the URLClassLoader.
430: */
431: @Override
432: public void addURL(URL url) {
433: super .addURL(url);
434:
435: _pendingScanUrls.add(url);
436: }
437:
438: /**
439: * Adds a scan listener.
440: */
441: public void addScanListener(ScanListener listener) {
442: if (_scanListeners == null)
443: _scanListeners = new ArrayList<ScanListener>();
444:
445: _scanListeners.add(listener);
446:
447: ArrayList<URL> urlList = new ArrayList<URL>();
448: for (URL url : getURLs()) {
449: if (!_pendingScanUrls.contains(url))
450: urlList.add(url);
451: }
452:
453: if (urlList.size() > 0) {
454: try {
455: make();
456: } catch (Exception e) {
457: log().log(Level.WARNING, e.toString(), e);
458:
459: if (_configException == null)
460: _configException = e;
461: }
462:
463: ArrayList<ScanListener> selfList = new ArrayList<ScanListener>();
464: selfList.add(listener);
465: ScanManager scanManager = new ScanManager(selfList);
466:
467: for (URL url : urlList) {
468: scanManager.scan(this , url);
469: }
470: }
471: }
472:
473: /**
474: * Called when the <class-loader> completes.
475: */
476: public void validate() {
477: super .validate();
478: }
479:
480: @Override
481: public void scan() {
482: ArrayList<URL> urlList = new ArrayList<URL>(_pendingScanUrls);
483: _pendingScanUrls.clear();
484:
485: if (_scanListeners != null && urlList.size() > 0) {
486: try {
487: make();
488: } catch (Exception e) {
489: log().log(Level.WARNING, e.toString(), e);
490:
491: if (_configException == null)
492: _configException = e;
493: }
494:
495: ScanManager scanManager = new ScanManager(_scanListeners);
496:
497: for (URL url : urlList) {
498: scanManager.scan(this , url);
499: }
500: }
501:
502: sendAddLoaderEventImpl();
503: }
504:
505: /**
506: * Starts the config phase of the environment.
507: */
508: private void config() {
509: sendAddLoaderEvent();
510:
511: ArrayList<EnvironmentListener> listeners = getEnvironmentListeners();
512:
513: int size = listeners.size();
514: for (int i = 0; listeners != null && i < size; i++) {
515: EnvironmentListener listener = listeners.get(i);
516:
517: listener.environmentConfig(this );
518: }
519: }
520:
521: /**
522: * Marks the environment of the class loader as started. The
523: * class loader itself doesn't use this, but a callback might.
524: */
525: public void start() {
526: if (!_lifecycle.toStarting())
527: return;
528:
529: sendAddLoaderEvent();
530:
531: config();
532:
533: ArrayList<EnvironmentListener> listeners = getEnvironmentListeners();
534:
535: int size = listeners.size();
536: for (int i = 0; listeners != null && i < size; i++) {
537: EnvironmentListener listener = listeners.get(i);
538:
539: listener.environmentStart(this );
540: }
541:
542: _lifecycle.toActive();
543: }
544:
545: /**
546: * Stops the environment, closing down any resources.
547: *
548: * The resources are closed in the reverse order that they're started
549: */
550: public void stop() {
551: if (!_lifecycle.toDestroy())
552: return;
553:
554: ArrayList<EnvironmentListener> listeners = getEnvironmentListeners();
555:
556: Thread thread = Thread.currentThread();
557: ClassLoader oldLoader = thread.getContextClassLoader();
558: thread.setContextClassLoader(this );
559:
560: try {
561: // closing down in reverse
562: if (listeners != null) {
563: for (int i = listeners.size() - 1; i >= 0; i--) {
564: EnvironmentListener listener = listeners.get(i);
565:
566: try {
567: listener.environmentStop(this );
568: } catch (Throwable e) {
569: log().log(Level.WARNING, e.toString(), e);
570: }
571: }
572: }
573: } finally {
574: thread.setContextClassLoader(oldLoader);
575:
576: // drain the thread pool for GC
577: ResinThreadPoolExecutor.getThreadPool().stopEnvironment(
578: this );
579: }
580: }
581:
582: /**
583: * Copies the loader.
584: */
585: protected void replace(EnvironmentClassLoader source) {
586: super .replace(source);
587:
588: // XXX: might need separate class
589: _owner = source._owner;
590: _attributes = source._attributes;
591: if (source._listeners != null) {
592: if (_listeners == null)
593: _listeners = new ArrayList<EnvironmentListener>();
594: _listeners.addAll(source._listeners);
595: source._listeners.clear();
596: }
597:
598: /*
599: _isStarted = source._isStarted;
600: _isActive = source._isActive;
601: _isStopped = source._isStopped;
602: */
603: }
604:
605: /**
606: * Destroys the class loader.
607: */
608: public void destroy() {
609: try {
610: WeakStopListener stopListener = _stopListener;
611: _stopListener = null;
612:
613: // make sure it's stopped first
614: stop();
615:
616: super .destroy();
617:
618: ClassLoader parent = getParent();
619: for (; parent != null; parent = parent.getParent()) {
620: if (parent instanceof EnvironmentClassLoader) {
621: EnvironmentClassLoader loader = (EnvironmentClassLoader) parent;
622:
623: loader.removeListener(stopListener);
624: }
625: }
626: } finally {
627: _owner = null;
628: _attributes = null;
629: _listeners = null;
630: _scanListeners = null;
631: }
632: }
633:
634: public String toString() {
635: if (getId() != null)
636: return getClass().getSimpleName() + "[" + getId() + "]";
637: else {
638: return getClass().getSimpleName() + "[]";
639: }
640: }
641:
642: /**
643: * Initializes the environment
644: */
645: public static synchronized void initializeEnvironment() {
646: if (_isStaticInit)
647: return;
648:
649: _isStaticInit = true;
650:
651: ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
652:
653: Thread thread = Thread.currentThread();
654: ClassLoader oldLoader = thread.getContextClassLoader();
655: try {
656: thread.setContextClassLoader(systemLoader);
657:
658: // #2281
659: // PolicyImpl.init();
660:
661: EnvironmentStream.setStdout(System.out);
662: EnvironmentStream.setStderr(System.err);
663:
664: try {
665: Vfs.initJNI();
666: } catch (Throwable e) {
667: }
668:
669: if (System.getProperty("org.xml.sax.driver") == null)
670: System.setProperty("org.xml.sax.driver",
671: "com.caucho.xml.Xml");
672:
673: Properties props = System.getProperties();
674:
675: /*
676: if (props.get("java.util.logging.manager") == null) {
677: props.put("java.util.logging.manager",
678: "com.caucho.log.LogManagerImpl");
679: }
680: */
681:
682: ClassLoader envClassLoader = EnvironmentClassLoader.class
683: .getClassLoader();
684:
685: boolean isGlobalLoadable = false;
686: try {
687: Class cl = Class.forName(
688: "com.caucho.naming.InitialContextFactoryImpl",
689: false, systemLoader);
690:
691: isGlobalLoadable = (cl != null);
692: } catch (Exception e) {
693: log().log(Level.FINER, e.toString(), e);
694: }
695:
696: if (isGlobalLoadable) {
697: // These properties require Resin to be at the system loader
698:
699: if (props.get("java.naming.factory.initial") == null) {
700: props
701: .put("java.naming.factory.initial",
702: "com.caucho.naming.InitialContextFactoryImpl");
703: }
704:
705: props.put("java.naming.factory.url.pkgs",
706: "com.caucho.naming");
707:
708: EnvironmentProperties
709: .enableEnvironmentSystemProperties(true);
710:
711: String oldBuilder = props
712: .getProperty("javax.management.builder.initial");
713: if (oldBuilder == null) {
714: oldBuilder = "com.caucho.jmx.MBeanServerBuilderImpl";
715: props.put("javax.management.builder.initial",
716: oldBuilder);
717: }
718:
719: /*
720: props.put("javax.management.builder.initial",
721: "com.caucho.jmx.EnvironmentMBeanServerBuilder");
722: */
723:
724: if (MBeanServerFactory.findMBeanServer(null).size() == 0)
725: MBeanServerFactory.createMBeanServer("Resin");
726:
727: ManagementFactory.getPlatformMBeanServer();
728: }
729:
730: TransactionManagerImpl tm = TransactionManagerImpl
731: .getInstance();
732: // TransactionManagerImpl.setLocal(tm);
733: //Jndi.bindDeep("java:comp/TransactionManager", tm);
734:
735: UserTransactionProxy ut = UserTransactionProxy
736: .getInstance();
737:
738: Jndi.bindDeep("java:comp/env/jmx/MBeanServer", Jmx
739: .getGlobalMBeanServer());
740: Jndi.bindDeep("java:comp/env/jmx/GlobalMBeanServer", Jmx
741: .getGlobalMBeanServer());
742:
743: Jndi.bindDeep("java:comp/UserTransaction", ut);
744:
745: // server/16g0
746: // Applications are incorrectly using TransactionManager
747: // as an extended UserTransaction
748: Jndi.bindDeep("java:comp/TransactionManager", tm);
749: Jndi.bindDeep("java:/TransactionManager", tm);
750:
751: Jndi.bindDeep("java:comp/ThreadPool",
752: ResinThreadPoolExecutor.getThreadPool());
753:
754: /*
755: try {
756: Jndi.rebindDeep("java:comp/ORB",
757: new com.caucho.iiop.orb.ORBImpl());
758: } catch (Exception e) {
759: e.printStackTrace();
760: }
761: */
762:
763: // J2EEManagedObject.register(new JTAResource(tm));
764: } catch (NamingException e) {
765: log().log(Level.FINE, e.toString(), e);
766: } catch (Throwable e) {
767: e.printStackTrace();
768: } finally {
769: thread.setContextClassLoader(oldLoader);
770: }
771: }
772:
773: private static final Logger log() {
774: if (_log == null)
775: _log = Logger.getLogger(EnvironmentClassLoader.class
776: .getName());
777:
778: return _log;
779: }
780: }
|