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.server.dispatch;
031:
032: import com.caucho.config.*;
033: import com.caucho.config.program.ConfigProgram;
034: import com.caucho.config.program.ContainerProgram;
035: import com.caucho.config.program.NodeBuilderProgram;
036: import com.caucho.config.types.InitParam;
037: import com.caucho.jmx.Jmx;
038: import com.caucho.jsp.Page;
039: import com.caucho.jsp.QServlet;
040: import com.caucho.naming.Jndi;
041: import com.caucho.remote.server.*;
042: import com.caucho.server.connection.StubServletRequest;
043: import com.caucho.server.connection.StubServletResponse;
044: import com.caucho.server.webapp.WebApp;
045: import com.caucho.servlet.comet.CometServlet;
046: import com.caucho.util.*;
047: import com.caucho.webbeans.component.*;
048: import com.caucho.webbeans.manager.*;
049:
050: import javax.annotation.PostConstruct;
051: import javax.naming.NamingException;
052: import javax.servlet.*;
053: import java.lang.reflect.Constructor;
054: import java.lang.reflect.Modifier;
055: import java.util.Collections;
056: import java.util.Enumeration;
057: import java.util.HashMap;
058: import java.util.Hashtable;
059: import java.util.Map;
060: import java.util.logging.Level;
061: import java.util.logging.Logger;
062:
063: /**
064: * Configuration for a servlet.
065: */
066: public class ServletConfigImpl implements ServletConfig, AlarmListener {
067: static L10N L = new L10N(ServletConfigImpl.class);
068: protected static final Logger log = Logger
069: .getLogger(ServletConfigImpl.class.getName());
070:
071: private String _location;
072:
073: private String _jndiName;
074: private String _var;
075:
076: private String _servletName;
077: private String _servletClassName;
078: private Class _servletClass;
079: private String _jspFile;
080: private String _displayName;
081: private int _loadOnStartup = Integer.MIN_VALUE;
082:
083: private boolean _allowEL = true;
084: private HashMap<String, String> _initParams = new HashMap<String, String>();
085:
086: private HashMap<String, String> _roleMap;
087:
088: private ContainerProgram _init;
089:
090: private RunAt _runAt;
091:
092: private ServletProtocolConfig _protocolConfig;
093: private ProtocolServletFactory _protocolFactory;
094:
095: private Alarm _alarm;
096: private ComponentImpl _comp;
097:
098: private ServletContext _servletContext;
099: private ServletManager _servletManager;
100:
101: private ServletException _initException;
102: private long _nextInitTime;
103:
104: private Object _servlet;
105: private FilterChain _servletChain;
106:
107: /**
108: * Creates a new servlet configuration object.
109: */
110: public ServletConfigImpl() {
111: }
112:
113: /**
114: * Sets the config location.
115: */
116: public void setConfigLocation(String location, int line) {
117: _location = location + ":" + line + ": ";
118: }
119:
120: /**
121: * Sets the id attribute
122: */
123: public void setId(String id) {
124: }
125:
126: /**
127: * Sets the servlet name.
128: */
129: public void setServletName(String name) {
130: _servletName = name;
131: }
132:
133: /**
134: * Gets the servlet name.
135: */
136: public String getServletName() {
137: return _servletName;
138: }
139:
140: /**
141: * Gets the servlet name.
142: */
143: public String getServletClassName() {
144: return _servletClassName;
145: }
146:
147: /**
148: * Sets the servlet class.
149: */
150: public void setServletClass(String servletClassName) {
151: _servletClassName = servletClassName;
152:
153: // JSF is special
154: if ("javax.faces.webapp.FacesServlet".equals(_servletClassName)) {
155: if (_loadOnStartup < 0)
156: _loadOnStartup = 1;
157:
158: if (_servletContext instanceof WebApp)
159: ((WebApp) _servletContext).createJsp()
160: .setLoadTldOnInit(true);
161: }
162: }
163:
164: /**
165: * Gets the servlet name.
166: */
167: public Class getServletClass() {
168: if (_servletClassName == null)
169: return null;
170:
171: if (_servletClass == null) {
172: try {
173: Thread thread = Thread.currentThread();
174: ClassLoader loader = thread.getContextClassLoader();
175:
176: _servletClass = Class.forName(_servletClassName, false,
177: loader);
178: } catch (Exception e) {
179: throw error(
180: L
181: .l(
182: "'{0}' is not a known servlet class. Servlets belong in the classpath, for example WEB-INF/classes.",
183: _servletClassName), e);
184: }
185: }
186:
187: return _servletClass;
188: }
189:
190: /**
191: * Sets the JSP file
192: */
193: public void setJspFile(String jspFile) {
194: _jspFile = jspFile;
195: }
196:
197: /**
198: * Gets the JSP file
199: */
200: public String getJspFile() {
201: return _jspFile;
202: }
203:
204: /**
205: * Sets the allow value.
206: */
207: public void setAllowEL(boolean allowEL) {
208: _allowEL = allowEL;
209: }
210:
211: /**
212: * Sets an init-param
213: */
214: public void setInitParam(String param, String value) {
215: _initParams.put(param, value);
216: }
217:
218: /**
219: * Sets an init-param
220: */
221: public InitParam createInitParam() {
222: InitParam initParam = new InitParam();
223:
224: initParam.setAllowEL(_allowEL);
225:
226: return initParam;
227: }
228:
229: /**
230: * Sets an init-param
231: */
232: public void setInitParam(InitParam initParam) {
233: _initParams.putAll(initParam.getParameters());
234: }
235:
236: /**
237: * Gets the init params
238: */
239: public Map getInitParamMap() {
240: return _initParams;
241: }
242:
243: /**
244: * Gets the init params
245: */
246: public String getInitParameter(String name) {
247: return _initParams.get(name);
248: }
249:
250: /**
251: * Gets the init params
252: */
253: public Enumeration getInitParameterNames() {
254: return Collections.enumeration(_initParams.keySet());
255: }
256:
257: /**
258: * Returns the servlet context.
259: */
260: public ServletContext getServletContext() {
261: return _servletContext;
262: }
263:
264: /**
265: * Sets the servlet context.
266: */
267: public void setServletContext(ServletContext app) {
268: _servletContext = app;
269: }
270:
271: /**
272: * Returns the servlet manager.
273: */
274: public ServletManager getServletManager() {
275: return _servletManager;
276: }
277:
278: /**
279: * Sets the servlet manager.
280: */
281: public void setServletManager(ServletManager manager) {
282: _servletManager = manager;
283: }
284:
285: /**
286: * Sets the init block
287: */
288: public void setInit(ContainerProgram init) {
289: _init = init;
290: }
291:
292: /**
293: * Gets the init block
294: */
295: public ContainerProgram getInit() {
296: return _init;
297: }
298:
299: /**
300: * Sets the load-on-startup
301: */
302: public void setLoadOnStartup(int loadOnStartup) {
303: _loadOnStartup = loadOnStartup;
304: }
305:
306: /**
307: * Gets the load-on-startup value.
308: */
309: public int getLoadOnStartup() {
310: if (_loadOnStartup > Integer.MIN_VALUE)
311: return _loadOnStartup;
312: else if (_runAt != null)
313: return 0;
314: else
315: return Integer.MIN_VALUE;
316: }
317:
318: /**
319: * Creates the run-at configuration.
320: */
321: public RunAt createRunAt() {
322: if (_runAt == null)
323: _runAt = new RunAt();
324:
325: return _runAt;
326: }
327:
328: public void setJndiName(String jndiName) {
329: _jndiName = jndiName;
330: }
331:
332: public void setVar(String var) {
333: _var = var;
334: }
335:
336: /**
337: * Returns the run-at configuration.
338: */
339: public RunAt getRunAt() {
340: return _runAt;
341: }
342:
343: /**
344: * Adds a security role reference.
345: */
346: public void addSecurityRoleRef(SecurityRoleRef ref) {
347: if (_roleMap == null)
348: _roleMap = new HashMap<String, String>(8);
349:
350: // server/12h2
351: // server/12m0
352: _roleMap.put(ref.getRoleName(), ref.getRoleLink());
353: }
354:
355: /**
356: * Adds a security role reference.
357: */
358: public HashMap<String, String> getRoleMap() {
359: return _roleMap;
360: }
361:
362: /**
363: * Sets the display name
364: */
365: public void setDisplayName(String displayName) {
366: _displayName = displayName;
367: }
368:
369: /**
370: * Gets the display name
371: */
372: public String getDisplayName() {
373: return _displayName;
374: }
375:
376: /**
377: * Sets the description
378: */
379: public void setDescription(String description) {
380: }
381:
382: /**
383: * Sets the icon
384: */
385: public void setIcon(com.caucho.config.types.Icon icon) {
386: }
387:
388: /**
389: * Sets the web service protocol.
390: */
391: public void setProtocol(ServletProtocolConfig protocol) {
392: _protocolConfig = protocol;
393: }
394:
395: /**
396: * Sets the init exception
397: */
398: public void setInitException(ServletException exn) {
399: _initException = exn;
400:
401: _nextInitTime = Long.MAX_VALUE / 2;
402:
403: if (exn instanceof UnavailableException) {
404: UnavailableException unExn = (UnavailableException) exn;
405:
406: if (!unExn.isPermanent())
407: _nextInitTime = (Alarm.getCurrentTime() + 1000L * unExn
408: .getUnavailableSeconds());
409: }
410: }
411:
412: /**
413: * Returns the servlet.
414: */
415: public Object getServlet() {
416: return _servlet;
417: }
418:
419: /**
420: * Initialize the servlet config.
421: */
422: @PostConstruct
423: public void init() throws ServletException {
424: if (_runAt != null) {
425: _alarm = new Alarm(this );
426: }
427:
428: if (_servletName != null) {
429: } else if (_protocolConfig != null) {
430: String protocolName = _protocolConfig.getUri();
431:
432: setServletName(_servletClassName + "-" + protocolName);
433: } else
434: setServletName(_servletClassName);
435:
436: // XXX: should only be for web services
437: if (_jndiName != null) {
438: validateClass(true);
439:
440: Object servlet = createServlet(false);
441:
442: try {
443: Jndi.bindDeepShort(_jndiName, servlet);
444: } catch (NamingException e) {
445: throw new ServletException(e);
446: }
447: }
448:
449: if (_var != null) {
450: validateClass(true);
451:
452: Object servlet = createServlet(false);
453:
454: WebBeansContainer webBeans = WebBeansContainer.create();
455: webBeans.addSingleton(servlet, _var);
456: }
457: }
458:
459: protected void validateClass(boolean requireClass)
460: throws ServletException {
461: if (_runAt != null || _loadOnStartup >= 0)
462: requireClass = true;
463:
464: Thread thread = Thread.currentThread();
465: ClassLoader loader = thread.getContextClassLoader();
466:
467: if (_servletClassName == null) {
468: } else if (_servletClassName.equals("invoker")) {
469: } else {
470: try {
471: _servletClass = Class.forName(_servletClassName, false,
472: loader);
473: } catch (ClassNotFoundException e) {
474: if (e instanceof CompileException)
475: throw error(e);
476:
477: log.log(Level.FINER, e.toString(), e);
478: }
479:
480: if (_servletClass != null) {
481: } else if (requireClass) {
482: throw error(L
483: .l(
484: "'{0}' is not a known servlet. Servlets belong in the classpath, often in WEB-INF/classes.",
485: _servletClassName));
486: } else {
487: String location = _location != null ? _location : "";
488:
489: log
490: .warning(L
491: .l(
492: location
493: + "'{0}' is not a known servlet. Servlets belong in the classpath, often in WEB-INF/classes.",
494: _servletClassName));
495: return;
496: }
497:
498: Config.checkCanInstantiate(_servletClass);
499:
500: if (Servlet.class.isAssignableFrom(_servletClass)) {
501: } else if (_protocolConfig != null) {
502: }
503: /*
504: else if (_servletClass.isAnnotationPresent(WebService.class)) {
505: // update protocol for "soap"?
506: }
507: else if (_servletClass.isAnnotationPresent(WebServiceProvider.class)) {
508: // update protocol for "soap"?
509: }
510: */
511: else
512: throw error(L
513: .l(
514: "'{0}' must implement javax.servlet.Servlet or have a <protocol>. All servlets must implement the Servlet interface.",
515: _servletClassName));
516:
517: /*
518: if (Modifier.isAbstract(_servletClass.getModifiers()))
519: throw error(L.l("'{0}' must not be abstract. Servlets must be fully-implemented classes.", _servletClassName));
520:
521: if (! Modifier.isPublic(_servletClass.getModifiers()))
522: throw error(L.l("'{0}' must be public. Servlets must be public classes.", _servletClassName));
523:
524: checkConstructor();
525: */
526: }
527: }
528:
529: /**
530: * Checks the class constructor for the public-zero arg.
531: */
532: public void checkConstructor() throws ServletException {
533: Constructor[] constructors = _servletClass
534: .getDeclaredConstructors();
535:
536: Constructor zeroArg = null;
537: for (int i = 0; i < constructors.length; i++) {
538: if (constructors[i].getParameterTypes().length == 0) {
539: zeroArg = constructors[i];
540: break;
541: }
542: }
543:
544: if (zeroArg == null)
545: throw error(L
546: .l(
547: "'{0}' must have a zero arg constructor. Servlets must have public zero-arg constructors.\n{1} is not a valid constructor.",
548: _servletClassName, constructors[0]));
549:
550: if (!Modifier.isPublic(zeroArg.getModifiers()))
551: throw error(L
552: .l(
553: "'{0}' must be public. '{1}' must have a public, zero-arg constructor.",
554: zeroArg, _servletClassName));
555: }
556:
557: /**
558: * Handles a cron alarm callback.
559: */
560: public void handleAlarm(Alarm alarm) {
561: try {
562: log.fine(this + " cron");
563:
564: FilterChain chain = createServletChain();
565:
566: ServletRequest req = new StubServletRequest();
567: ServletResponse res = new StubServletResponse();
568:
569: chain.doFilter(req, res);
570: } catch (Throwable e) {
571: log.log(Level.WARNING, e.toString(), e);
572: } finally {
573: long nextTime = _runAt.getNextTimeout(Alarm
574: .getCurrentTime());
575: _alarm.queue(nextTime - Alarm.getCurrentTime());
576: }
577: }
578:
579: public FilterChain createServletChain() throws ServletException {
580: synchronized (this ) {
581: // JSP files need to have separate chains created for each JSP
582:
583: if (_servletChain != null)
584: return _servletChain;
585: else
586: return createServletChainImpl();
587: }
588: }
589:
590: private FilterChain createServletChainImpl()
591: throws ServletException {
592: String jspFile = getJspFile();
593: FilterChain servletChain = null;
594:
595: if (jspFile != null) {
596: QServlet jsp = (QServlet) _servletManager
597: .createServlet("resin-jsp");
598:
599: servletChain = new PageFilterChain(_servletContext, jsp,
600: jspFile, this );
601:
602: return servletChain;
603: }
604:
605: validateClass(true);
606:
607: Class servletClass = getServletClass();
608:
609: if (servletClass == null) {
610: throw new IllegalStateException(L.l(
611: "servlet class for {0} can't be null",
612: getServletName()));
613: } else if (QServlet.class.isAssignableFrom(servletClass)) {
614: servletChain = new PageFilterChain(_servletContext,
615: (QServlet) createServlet(false));
616: } else if (SingleThreadModel.class
617: .isAssignableFrom(servletClass)) {
618: servletChain = new SingleThreadServletFilterChain(this );
619: } else if (_protocolConfig != null) {
620: servletChain = new WebServiceFilterChain(this );
621: } else if (CometServlet.class.isAssignableFrom(servletClass))
622: servletChain = new CometServletFilterChain(this );
623: else {
624: servletChain = new ServletFilterChain(this );
625: }
626:
627: if (_roleMap != null)
628: servletChain = new SecurityRoleMapFilterChain(servletChain,
629: _roleMap);
630:
631: // server/10a8. JSP pages need a fresh PageFilterChain
632: // XXX: lock contention issues with JSPs?
633: /*
634: if (! QServlet.class.isAssignableFrom(servletClass))
635: _servletChain = servletChain;
636: */
637:
638: return servletChain;
639: }
640:
641: /**
642: * Instantiates a web service.
643: *
644: * @return the initialized servlet.
645: */
646: /*
647: ProtocolServlet createWebServiceSkeleton()
648: throws ServletException
649: {
650: try {
651: Object service = createServlet(false);
652:
653: ProtocolServlet skeleton
654: = (ProtocolServlet) _protocolClass.newInstance();
655:
656: skeleton.setService(service);
657:
658: if (_protocolInit != null) {
659: _protocolInit.configure(skeleton);
660: }
661:
662: skeleton.init(this);
663:
664: return skeleton;
665: } catch (RuntimeException e) {
666: throw e;
667: } catch (ServletException e) {
668: throw e;
669: } catch (Exception e) {
670: throw new ServletException(e);
671: }
672: }
673: */
674:
675: /**
676: * Instantiates a servlet given its configuration.
677: *
678: * @param servletName the servlet
679: *
680: * @return the initialized servlet.
681: */
682: Object createServlet(boolean isNew) throws ServletException {
683: // server/102e
684: if (_servlet != null && !isNew)
685: return _servlet;
686:
687: Object servlet = null;
688:
689: if (Alarm.getCurrentTime() < _nextInitTime)
690: throw _initException;
691:
692: try {
693: synchronized (this ) {
694: if (!isNew && _servlet != null)
695: return _servlet;
696:
697: // XXX: this was outside of the sync block
698: servlet = createServletImpl();
699:
700: if (!isNew)
701: _servlet = servlet;
702: }
703:
704: if (log.isLoggable(Level.FINE))
705: log.finer("Servlet[" + _servletName + "] active");
706:
707: //J2EEManagedObject.register(new com.caucho.management.j2ee.Servlet(this));
708:
709: if (!isNew) {
710: // If the servlet has an MBean, register it
711: try {
712: Hashtable<String, String> props = new Hashtable<String, String>();
713:
714: props.put("type", _servlet.getClass()
715: .getSimpleName());
716: props.put("name", _servletName);
717: Jmx.register(_servlet, props);
718: } catch (Exception e) {
719: log.finest(e.toString());
720: }
721:
722: if (_runAt != null && _alarm != null) {
723: long nextTime = _runAt.getNextTimeout(Alarm
724: .getCurrentTime());
725: _alarm.queue(nextTime - Alarm.getCurrentTime());
726: }
727: }
728:
729: if (log.isLoggable(Level.FINE))
730: log.finer("Servlet[" + _servletName + "] active");
731:
732: return servlet;
733: } catch (ServletException e) {
734: throw e;
735: } catch (Throwable e) {
736: throw new ServletException(e);
737: }
738: }
739:
740: Servlet createProtocolServlet() throws ServletException {
741: try {
742: Object service = createServletImpl();
743:
744: if (_protocolFactory == null)
745: _protocolFactory = _protocolConfig.createFactory();
746:
747: Servlet servlet = _protocolFactory.createServlet(
748: _servletClass, service);
749:
750: servlet.init(this );
751:
752: return servlet;
753: } catch (RuntimeException e) {
754: throw e;
755: } catch (ServletException e) {
756: throw e;
757: } catch (Exception e) {
758: throw new ServletException(e);
759: }
760: }
761:
762: private Object createServletImpl() throws Exception {
763: Class servletClass = getServletClass();
764:
765: Object servlet;
766: if (_jspFile != null) {
767: servlet = createJspServlet(_servletName, _jspFile);
768:
769: if (servlet == null)
770: throw new ServletException(L.l(
771: "'{0}' is a missing JSP file.", _jspFile));
772: }
773:
774: else if (servletClass != null) {
775: WebBeansContainer webBeans = WebBeansContainer.create();
776:
777: _comp = (ComponentImpl) webBeans
778: .createTransient(servletClass);
779:
780: servlet = _comp.createNoInit();
781: } else
782: throw new ServletException(L.l(
783: "Null servlet class for '{0}'.", _servletName));
784:
785: configureServlet(servlet);
786:
787: try {
788: if (servlet instanceof Page) {
789: // server/102i
790: // page already configured
791: } else if (servlet instanceof Servlet) {
792: Servlet servletObj = (Servlet) servlet;
793:
794: servletObj.init(this );
795: }
796: } catch (UnavailableException e) {
797: setInitException(e);
798: throw e;
799: }
800:
801: return servlet;
802: }
803:
804: /**
805: * Configure the servlet (everything that is done after
806: * instantiation but before servlet.init()
807: */
808: void configureServlet(Object servlet) {
809: //InjectIntrospector.configure(servlet);
810:
811: // Initialize bean properties
812: ConfigProgram init = getInit();
813:
814: if (init != null)
815: init.configure(servlet);
816:
817: Config.init(servlet);
818: }
819:
820: /**
821: * Instantiates a servlet given its configuration.
822: *
823: * @param servletName the servlet
824: *
825: * @return the initialized servlet.
826: */
827: private Servlet createJspServlet(String servletName, String jspFile)
828: throws ServletException {
829: try {
830: ServletConfigImpl jspConfig = _servletManager
831: .getServlet("resin-jsp");
832:
833: QServlet jsp = (QServlet) jspConfig.createServlet(false);
834:
835: // server/105o
836: Page page = jsp.getPage(servletName, jspFile, this );
837:
838: return page;
839: } catch (ServletException e) {
840: throw e;
841: } catch (Exception e) {
842: throw new ServletException(e);
843: }
844: }
845:
846: void killServlet() {
847: Object servlet = _servlet;
848: _servlet = null;
849:
850: Alarm alarm = _alarm;
851: _alarm = null;
852:
853: if (alarm != null)
854: alarm.dequeue();
855:
856: if (_comp != null)
857: _comp.destroy(servlet);
858:
859: if (servlet instanceof Servlet) {
860: ((Servlet) servlet).destroy();
861: }
862: }
863:
864: public void close() {
865: killServlet();
866:
867: _alarm = null;
868: }
869:
870: protected ConfigException error(String msg) {
871: if (_location != null)
872: return new LineConfigException(_location + msg);
873: else
874: return new ConfigException(msg);
875: }
876:
877: protected ConfigException error(String msg, Throwable e) {
878: if (_location != null)
879: return new LineConfigException(_location + msg, e);
880: else
881: return new ConfigException(msg, e);
882: }
883:
884: protected RuntimeException error(Throwable e) {
885: if (_location != null)
886: return new LineConfigException(_location + e.getMessage(),
887: e);
888: else
889: return ConfigException.create(e);
890: }
891:
892: /**
893: * Returns a printable representation of the servlet config object.
894: */
895: public String toString() {
896: return "ServletConfigImpl[name=" + _servletName + ",class="
897: + _servletClass + "]";
898: }
899: }
|