001: /*
002: License $Id: JoAbstractModel.java,v 1.11 2005/01/05 18:08:40 hendriks73 Exp $
003:
004: Copyright (c) 2001-2005 tagtraum industries.
005:
006: LGPL
007: ====
008:
009: jo! is free software; you can redistribute it and/or
010: modify it under the terms of the GNU Lesser General Public
011: License as published by the Free Software Foundation; either
012: version 2.1 of the License, or (at your option) any later version.
013:
014: jo! 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. See the GNU
017: Lesser General Public License for more details.
018:
019: You should have received a copy of the GNU Lesser General Public
020: License along with this library; if not, write to the Free Software
021: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022:
023: For LGPL see <http://www.fsf.org/copyleft/lesser.txt>
024:
025:
026: Sun license
027: ===========
028:
029: This release contains software by Sun Microsystems. Therefore
030: the following conditions have to be met, too. They apply to the
031: files
032:
033: - lib/mail.jar
034: - lib/activation.jar
035: - lib/jsse.jar
036: - lib/jcert.jar
037: - lib/jaxp.jar
038: - lib/crimson.jar
039: - lib/servlet.jar
040: - lib/jnet.jar
041: - lib/jaas.jar
042: - lib/jaasmod.jar
043:
044: contained in this release.
045:
046: a. Licensee may not modify the Java Platform
047: Interface (JPI, identified as classes contained within the javax
048: package or any subpackages of the javax package), by creating additional
049: classes within the JPI or otherwise causing the addition to or modification
050: of the classes in the JPI. In the event that Licensee creates any
051: Java-related API and distribute such API to others for applet or
052: application development, you must promptly publish broadly, an accurate
053: specification for such API for free use by all developers of Java-based
054: software.
055:
056: b. Software is confidential copyrighted information of Sun and
057: title to all copies is retained by Sun and/or its licensors. Licensee
058: shall not modify, decompile, disassemble, decrypt, extract, or otherwise
059: reverse engineer Software. Software may not be leased, assigned, or
060: sublicensed, in whole or in part. Software is not designed or intended
061: for use in on-line control of aircraft, air traffic, aircraft navigation
062: or aircraft communications; or in the design, construction, operation or
063: maintenance of any nuclear facility. Licensee warrants that it will not
064: use or redistribute the Software for such purposes.
065:
066: c. Software is provided "AS IS," without a warranty
067: of any kind. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES,
068: INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
069: PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
070:
071: d. This License is effective until terminated. Licensee may
072: terminate this License at any time by destroying all copies of Software.
073: This License will terminate immediately without notice from Sun if Licensee
074: fails to comply with any provision of this License. Upon such termination,
075: Licensee must destroy all copies of Software.
076:
077: e. Software, including technical data, is subject to U.S.
078: export control laws, including the U.S. Export Administration Act and its
079: associated regulations, and may be subject to export or import regulations
080: in other countries. Licensee agrees to comply strictly with all such
081: regulations and acknowledges that it has the responsibility to obtain
082: licenses to export, re-export, or import Software. Software may not be
083: downloaded, or otherwise exported or re-exported (i) into, or to a national
084: or resident of, Cuba, Iraq, Iran, North Korea, Libya, Sudan, Syria or any
085: country to which the U.S. has embargoed goods; or (ii) to anyone on the
086: U.S. Treasury Department's list of Specially Designated Nations or the U.S.
087: Commerce Department's Table of Denial Orders.
088:
089:
090: Feedback
091: ========
092:
093: We encourage your feedback and suggestions and want to use your feedback to
094: improve the Software. Send all such feedback to:
095: <feedback@tagtraum.com>
096:
097: For more information on tagtraum industries and jo!
098: please see <http://www.tagtraum.com/>.
099:
100:
101: */
102: package com.tagtraum.jo;
103:
104: import com.tagtraum.framework.log.Log;
105: import com.tagtraum.framework.recycler.Recycler;
106: import com.tagtraum.framework.util.FactoryException;
107: import com.tagtraum.framework.util.I_Pool;
108: import com.tagtraum.jo.security.I_JoSecurityRoleRef;
109: import com.tagtraum.jo.util.ServletClassLoader;
110:
111: import javax.servlet.*;
112: import javax.swing.event.ChangeEvent;
113: import javax.swing.event.ChangeListener;
114: import java.io.IOException;
115: import java.io.ObjectInputStream;
116: import java.io.Serializable;
117: import java.net.URL;
118: import java.util.Enumeration;
119: import java.util.Hashtable;
120: import java.util.ResourceBundle;
121: import java.util.Vector;
122:
123: /**
124: * Encapsulates the servlet instance or instances.
125: *
126: * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
127: * @version 1.1beta1 $Id: JoAbstractModel.java,v 1.11 2005/01/05 18:08:40 hendriks73 Exp $
128: */
129: public abstract class JoAbstractModel implements Serializable,
130: I_JoServletModel, I_JoWebAppElement, C_Jo {
131:
132: /**
133: * Source-Version
134: */
135: public static String vcid = "$Id: JoAbstractModel.java,v 1.11 2005/01/05 18:08:40 hendriks73 Exp $";
136:
137: /**
138: * Localized Strings.
139: */
140: private static ResourceBundle localStrings = ResourceBundle
141: .getBundle("com.tagtraum.jo.localStrings");
142:
143: private transient String name;
144: private transient String description;
145: private transient String displayName;
146: private transient JoIcon icon;
147: private transient Hashtable securityRoleRefs;
148: private transient Hashtable securityRoleRefsByLink;
149: private transient int activeInstances;
150: private transient boolean loaded;
151: private transient boolean unloadFlag;
152: private transient Class servletClass;
153: private transient UnavailableException permanentlyUnavailable;
154: private transient long unavailableUntil;
155: private transient I_JoServletContextPeer servletContextPeer;
156: private transient I_JoHost host;
157: private transient ServletConfig servletConfig;
158: private transient ServletClassLoader classLoader;
159: private transient Vector changeListeners;
160:
161: protected transient int serviceMethodCalls;
162: protected transient Servlet servlet;
163:
164: private int maxActiveInstances;
165: private int destroyTimeout;
166: private boolean loadOnStartup;
167: private int loadOrderPosition;
168: private Hashtable initParameters;
169: private long lastModified;
170: private long lastAccess;
171: private String jspFileName;
172:
173: /**
174: * Flag that indicates whether this servlet is allowed to
175: * access the servlet engine.
176: */
177: private boolean privileged;
178: private String servletClassName;
179: private String unavailableReason;
180:
181: public JoAbstractModel() {
182: this .initParameters = new Hashtable();
183: this .unloadFlag = false;
184: this .icon = new JoIcon();
185: this .securityRoleRefs = new Hashtable();
186: this .securityRoleRefsByLink = new Hashtable();
187: this .changeListeners = new Vector();
188: }
189:
190: public void init() {
191: setMaxActiveInstances(1);
192: this .loadOnStartup = false;
193: this .loadOrderPosition = -1;
194: this .loaded = false;
195: this .permanentlyUnavailable = null;
196: this .activeInstances = 0;
197: this .description = null;
198: this .displayName = null;
199: this .securityRoleRefs.clear();
200: this .securityRoleRefsByLink.clear();
201: this .changeListeners.clear();
202: this .icon.init(null);
203: this .initParameters.clear();
204: this .destroyTimeout = C_DefaultDestroyTimeout;
205: this .privileged = false;
206: }
207:
208: private void readObject(ObjectInputStream in) throws IOException,
209: ClassNotFoundException {
210: // make sure objects can be preoperly deserialized
211: this .unloadFlag = false;
212: this .icon = new JoIcon();
213: this .securityRoleRefs = new Hashtable();
214: this .changeListeners = new Vector();
215: in.defaultReadObject();
216: }
217:
218: public void setClassLoader(ServletClassLoader classLoader) {
219: this .classLoader = classLoader;
220: // make sure the new classloader is actually being used.
221: setServletClass(null);
222: }
223:
224: /**
225: * Eventlistener for possible UI.
226: */
227: public void addChangeListener(ChangeListener cl) {
228: if (!changeListeners.contains(cl))
229: changeListeners.add(cl);
230: }
231:
232: /**
233: * Eventlistener for possible UI.
234: */
235: public boolean removeChangeListener(ChangeListener cl) {
236: return changeListeners.remove(cl);
237: }
238:
239: /**
240: * Eventlistener for possible UI.
241: */
242: private void fireChangeEvent() {
243: ChangeEvent ce = new ChangeEvent(this );
244: for (int i = 0; i < changeListeners.size(); i++) {
245: ((ChangeListener) changeListeners.get(i)).stateChanged(ce);
246: }
247: }
248:
249: /**
250: * Returns when the resources this model depends upon
251: * (source files, class files, ...) have been last modified.
252: */
253: public long getLastModified() {
254: return lastModified;
255: }
256:
257: /**
258: * Sets when the resources this model depends upon
259: * (source files, class files, ...) have been last modified.
260: */
261: public void setLastModified(long lastModified) {
262: this .lastModified = lastModified;
263: }
264:
265: public boolean isPrivileged() {
266: return privileged;
267: }
268:
269: public void setPrivileged(boolean privileged) {
270: this .privileged = privileged;
271: }
272:
273: /**
274: * Adds a number of URLs to the classloader.
275: *
276: * @param aURL array of URLs
277: */
278: public void addClassLocations(URL[] aURL) {
279: setClassLoader(new ServletClassLoader(aURL, classLoader));
280: }
281:
282: /**
283: * Returns the current classloader.
284: *
285: * @return classloader
286: */
287: public ClassLoader getClassLoader() {
288: return classLoader;
289: }
290:
291: /**
292: * Returns the class of the model.
293: *
294: * return class
295: */
296: public Class getServletClass() throws ClassNotFoundException {
297: if (servletClass == null) {
298: //System.out.println("Name: " + getName());
299: //System.out.println("Used classloader: " + classLoader);
300:
301: setServletClass(Recycler.getRecycler().getClassForName(
302: servletClassName, classLoader));
303: setLastModified(classLoader.lastModified(getServletClass()));
304: }
305: return servletClass;
306: }
307:
308: public void setServletClass(Class servletClass) {
309: this .servletClass = servletClass;
310: }
311:
312: public String getName() {
313: return name;
314: }
315:
316: public void setName(String name) {
317: this .name = name;
318: }
319:
320: /**
321: * Returns the classname of the servlet.
322: *
323: * return classname
324: */
325: public String getServletClassName() {
326: return servletClassName;
327: }
328:
329: /**
330: * Sets the classname.
331: *
332: * @param aClassName classname
333: */
334: public void setServletClassName(String aClassName) {
335: this .servletClassName = aClassName;
336: }
337:
338: public String getJspFileName() {
339: return jspFileName;
340: }
341:
342: public void setJspFileName(String jspFileName) {
343: this .jspFileName = jspFileName;
344: }
345:
346: /**
347: * Sets the maximum number of active instances. This is relevant for SingleThreadModels.
348: *
349: * @param max maximum number of instances
350: */
351: public void setMaxActiveInstances(int max) {
352: if (max < 1)
353: throw new IllegalArgumentException(localStrings
354: .getString("max_active_instances_less_than_one"));
355: this .maxActiveInstances = max;
356: }
357:
358: /**
359: * Returns the maximum number of active instances. This is relevant for SingleThreadModels.
360: *
361: * @return maximum number of instances
362: */
363: public int getMaxActiveInstances() {
364: return maxActiveInstances;
365: }
366:
367: /**
368: * Returns the number of active instances. This is relevant for SingleThreadModels.
369: *
370: * @return number of instances
371: */
372: public int getActiveInstances() {
373: return activeInstances;
374: }
375:
376: /**
377: * Sets the number of active instances. This is relevant for SingleThreadModels.
378: *
379: * @param activeInstances number of instances
380: */
381: protected void setActiveInstances(int activeInstances) {
382: this .activeInstances = activeInstances;
383: }
384:
385: /**
386: * Sets the time in ms that shall be waited before a servlet is destroyed although
387: * it is still executed.
388: *
389: * @param destroyTimeout timeout in ms.
390: */
391: public void setDestroyTimeout(int destroyTimeout) {
392: if (destroyTimeout < 0) {
393: throw new IllegalArgumentException(localStrings
394: .getString("destroy_timeout_less_than_zero"));
395: }
396: this .destroyTimeout = destroyTimeout;
397: }
398:
399: /**
400: * Returns the time in ms that shall be waited before a servlet is destroyed although
401: * it is still executed.
402: *
403: * @return time in ms.
404: */
405: public int getDestroyTimeout() {
406: return destroyTimeout;
407: }
408:
409: /**
410: * Returns the ServletConfig for this model.
411: *
412: * @return ServletConfig
413: */
414: public ServletConfig getServletConfig() {
415: return servletConfig;
416: }
417:
418: /**
419: * Sets the ServletConfig for this model.
420: *
421: * @param servletConfig
422: */
423: public void setServletConfig(ServletConfig servletConfig) {
424: this .servletConfig = servletConfig;
425: }
426:
427: /**
428: * Loads this servlet.
429: */
430: public void preload() throws FactoryException, ServletException,
431: IOException {
432: // make sure that the thread that preloads the servlets has
433: // the right Thread ContextClassloader
434: ClassLoader oldContextClassLoader = Thread.currentThread()
435: .getContextClassLoader();
436: try {
437: Thread.currentThread().setContextClassLoader(
438: getServletContextPeer().getClassLoader());
439: cacheServletInstance(getServletInstance());
440: } finally {
441: Thread.currentThread().setContextClassLoader(
442: oldContextClassLoader);
443: }
444: }
445:
446: /**
447: * Sets whether this servlet should be loaded as startup.
448: */
449: public void setLoadOnStartup(boolean loadOnStartup) {
450: this .loadOnStartup = loadOnStartup;
451: }
452:
453: /**
454: * Return whether this servlet should be loaded as startup.
455: */
456: public boolean isLoadOnStartup() {
457: return loadOnStartup;
458: }
459:
460: /**
461: * Sets the load order position.
462: */
463: public void setLoadOrderPosition(int loadOrderPosition) {
464: this .loadOrderPosition = loadOrderPosition;
465: }
466:
467: /**
468: * Returns the load order position.
469: */
470: public int getLoadOrderPosition() {
471: return loadOrderPosition;
472: }
473:
474: /**
475: * Returns a servlet instance.
476: */
477: protected abstract Servlet getServletInstance()
478: throws ServletException, IOException;
479:
480: /**
481: * Caches a servlet instance.
482: *
483: * @param servletInstance servlet instance
484: */
485: private void cacheServletInstance(Servlet servletInstance) {
486: if (servletInstance instanceof SingleThreadModel) {
487: Recycler.getRecycler().put(servletInstance);
488: activeInstances--;
489: synchronized (this ) {
490: notify();
491: }
492: } else {
493: this .servlet = servletInstance;
494: }
495: }
496:
497: /**
498: * Executes the service method of the servlet.
499: *
500: * @exception IOException
501: * @exception ServletException
502: */
503: public void service(ServletRequest request, ServletResponse response)
504: throws IOException, ServletException {
505: boolean initialized = false;
506: boolean unload = false;
507:
508: if (unloadFlag)
509: throw new UnavailableException(localStrings
510: .getString("servlet_is_being_destroyed"), 0);
511: serviceMethodCalls++;
512: // and go!
513:
514: ClassLoader oldContextClassLoader = Thread.currentThread()
515: .getContextClassLoader();
516: try {
517: Thread.currentThread().setContextClassLoader(
518: getServletContextPeer().getClassLoader());
519: Servlet servletInstance = getServletInstance();
520: initialized = true;
521: servletInstance.service(request, response);
522: cacheServletInstance(servletInstance); // put instance back into the pool
523: lastAccess = System.currentTimeMillis();
524: } catch (UnavailableException ue) {
525: if (initialized)
526: unload = true;
527: if (ue.isPermanent()) {
528: permanentlyUnavailable = ue;
529: } else {
530: unavailableUntil = System.currentTimeMillis()
531: + ue.getUnavailableSeconds() * 1000;
532: unavailableReason = ue.getMessage();
533: }
534: throw ue;
535: } finally {
536: serviceMethodCalls--;
537: // TODO: unload is always false... Review this!!
538: if (unload) {
539: unload();
540: }
541: Thread.currentThread().setContextClassLoader(
542: oldContextClassLoader);
543: }
544: }
545:
546: public void setPermanentlyUnavailable(
547: UnavailableException permanentlyUnavailable) {
548: this .permanentlyUnavailable = permanentlyUnavailable;
549: }
550:
551: /**
552: * Indicates whether this servlet is loaded or not.
553: *
554: * @return true or false.
555: */
556: public boolean isLoaded() {
557: return loaded;
558: }
559:
560: /**
561: * Sets whether this servlet is loaded or not.
562: */
563: protected void setLoaded(boolean loaded) {
564: if (loaded != this .loaded) {
565: this .loaded = loaded;
566: fireChangeEvent();
567: }
568: }
569:
570: /**
571: * Destroys all instances of this servlet.
572: */
573: public synchronized void unload() {
574: if (!loaded)
575: return;
576: unloadFlag = true;
577: long timeWaited = 0;
578: while (serviceMethodCalls > 0 && timeWaited < destroyTimeout) {
579: try {
580: wait(100); // wait in 100ms steps
581: timeWaited += 100;
582: } catch (InterruptedException ignore) {
583: }
584: }
585: destroy();
586: unloadFlag = false;
587: }
588:
589: /**
590: * Last time this servlet was executed.
591: */
592: public long lastAccessed() {
593: return lastAccess;
594: }
595:
596: /**
597: * Executes the real destruction of the instances.
598: *
599: * @see #unload
600: */
601: private void destroy() {
602: try {
603: if (servlet != null) {
604: servlet.destroy();
605: servlet = null;
606: } else {
607: Recycler aRecycler = Recycler.getRecycler();
608: I_Pool aPool = aRecycler
609: .getPool(aRecycler.getClassForName(
610: servletClassName, classLoader));
611: Servlet anInstance;
612: while ((anInstance = (Servlet) aPool.get()) != null) {
613: anInstance.destroy();
614: }
615: }
616: setLoaded(false);
617: } catch (Throwable t) {
618: if (Log.isLog(host.getName())) {
619: Log
620: .getLog(host.getName())
621: .log(
622: localStrings
623: .getString("exception_while_destroying_servlet")
624: + " "
625: + servletClassName
626: + ": "
627: + t.toString(), Log.ERROR);
628: }
629: Log.getLog(host.getName()).log(t, Log.ERROR);
630: }
631: this .serviceMethodCalls = 0;
632: this .activeInstances = 0;
633: }
634:
635: /**
636: * Returns a configuration parameter.
637: *
638: * @return a value
639: */
640: public String getInitParameter(String aKey) {
641: return (String) initParameters.get(aKey);
642: }
643:
644: /**
645: * Sets an init parameter. If aValue is null the parameter
646: * is deleted.
647: *
648: * @param aKey a key
649: * @param aValue a value
650: */
651: public void setInitParameter(String aKey, String aValue) {
652: initParameters.put(aKey, aValue);
653: }
654:
655: /**
656: * Removes an init parameter.
657: */
658: public String removeInitParameter(String name) {
659: return (String) initParameters.remove(name);
660: }
661:
662: /**
663: * Returns the names for all init parameters.
664: *
665: * @return an Enumeration of names
666: */
667: public Enumeration getInitParameterNames() {
668: return initParameters.keys();
669: }
670:
671: /**
672: * Returns the <code>ServletContext</code>.
673: *
674: * @return ServletContext
675: */
676: public ServletContext getServletContext() {
677: return servletContextPeer.getServletContext();
678: }
679:
680: /**
681: * Returns the <code>ServletContextPeer</code>.
682: *
683: * @return ServletContextPeer
684: */
685: public I_JoServletContextPeer getServletContextPeer() {
686: return servletContextPeer;
687: }
688:
689: public void setServletContextPeer(
690: I_JoServletContextPeer servletContextPeer) {
691: this .servletContextPeer = servletContextPeer;
692: this .host = servletContextPeer.getHost();
693: setClassLoader((ServletClassLoader) servletContextPeer
694: .getClassLoader());
695: try {
696: ServletConfig config = (ServletConfig) Recycler
697: .getNamedRecycler(host.getName()).get(
698: C_FactoryKey_ServletConfig);
699: ((I_JoServletConfig) config).init(this );
700: setServletConfig(config);
701: } catch (FactoryException e) {
702: // ignore for now...
703: e.printStackTrace();
704: }
705: }
706:
707: /**
708: * Adds a SecurityRoleReference.
709: */
710: public void addSecurityRoleRef(I_JoSecurityRoleRef securityRoleRef) {
711: this .securityRoleRefs.put(securityRoleRef.getRoleName(),
712: securityRoleRef);
713: if (securityRoleRef.getRoleLink() != null) {
714: this .securityRoleRefsByLink.put(securityRoleRef
715: .getRoleLink(), securityRoleRef);
716: }
717: }
718:
719: /**
720: * Returns a SecurityRoleReference.
721: */
722: public I_JoSecurityRoleRef getSecurityRoleRef(String aRoleName) {
723: return (I_JoSecurityRoleRef) securityRoleRefs.get(aRoleName);
724: }
725:
726: public I_JoSecurityRoleRef getSecurityRoleRefByLink(String aRoleLink) {
727: return (I_JoSecurityRoleRef) securityRoleRefsByLink
728: .get(aRoleLink);
729: }
730:
731: /**
732: * Returns the display name.
733: *
734: * @return display name
735: */
736: public String getDisplayName() {
737: return displayName;
738: }
739:
740: /**
741: * Sets the display name.
742: *
743: * @param aName display name
744: */
745: public void setDisplayName(String aName) {
746: this .displayName = aName;
747: }
748:
749: /**
750: * Returns optional description.
751: *
752: * @return description or <code>null</code>
753: */
754: public String getDescription() {
755: return description;
756: }
757:
758: /**
759: * Sets optional description.
760: *
761: * @param aDescription description
762: */
763: public void setDescription(String aDescription) {
764: this .description = aDescription;
765: }
766:
767: /**
768: * Returns the icon.
769: *
770: * @return icon
771: */
772: public JoIcon getIcon() {
773: return icon;
774: }
775:
776: /**
777: * Sets the icon.
778: *
779: * @param aIcon a icon
780: */
781: public void setIcon(JoIcon aIcon) {
782: this .icon = aIcon;
783: }
784:
785: protected void checkUnavailable() throws UnavailableException {
786: if (permanentlyUnavailable != null)
787: throw permanentlyUnavailable;
788: final long now = System.currentTimeMillis();
789: if (unavailableUntil != 0 && now < unavailableUntil) {
790: throw new UnavailableException(unavailableReason,
791: (int) (unavailableUntil - now) / 1000);
792: } else {
793: unavailableUntil = 0;
794: unavailableReason = null;
795: }
796: }
797:
798: protected synchronized void waitForAvailableInstance() {
799: while (getActiveInstances() >= getMaxActiveInstances()) {
800: try {
801: wait();
802: } catch (InterruptedException ie) {
803: // ignore
804: }
805: }
806: }
807: }
|