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.host;
031:
032: import com.caucho.lifecycle.Lifecycle;
033: import com.caucho.loader.EnvironmentClassLoader;
034: import com.caucho.log.Log;
035: import com.caucho.make.AlwaysModified;
036: import com.caucho.server.cluster.Server;
037: import com.caucho.server.deploy.DeployContainer;
038: import com.caucho.server.dispatch.DispatchBuilder;
039: import com.caucho.server.dispatch.DispatchServer;
040: import com.caucho.server.dispatch.ErrorFilterChain;
041: import com.caucho.server.dispatch.Invocation;
042: import com.caucho.server.e_app.EarConfig;
043: import com.caucho.server.rewrite.RewriteDispatch;
044: import com.caucho.server.webapp.*;
045: import com.caucho.util.L10N;
046: import com.caucho.vfs.Path;
047: import com.caucho.vfs.Vfs;
048:
049: import javax.servlet.FilterChain;
050: import java.util.ArrayList;
051: import java.util.HashMap;
052: import java.util.logging.Level;
053: import java.util.logging.Logger;
054:
055: /**
056: * Resin's host container implementation.
057: */
058: public class HostContainer implements DispatchBuilder {
059: private static final Logger log = Logger
060: .getLogger(HostContainer.class.getName());
061: private static final L10N L = new L10N(HostContainer.class);
062:
063: // The environment class loader
064: private EnvironmentClassLoader _classLoader;
065:
066: private DispatchServer _dispatchServer;
067:
068: private WebApp _errorWebApp;
069:
070: private String _url = "";
071:
072: // The root directory.
073: private Path _rootDir;
074:
075: // dispatch mapping
076: private RewriteDispatch _rewriteDispatch;
077:
078: // List of default host configurations
079: private ArrayList<HostConfig> _hostDefaultList = new ArrayList<HostConfig>();
080:
081: // The host deploy
082: private DeployContainer<HostController> _hostDeploy = new DeployContainer<HostController>();
083:
084: // Cache of hosts
085: private HashMap<String, HostController> _hostMap = new HashMap<String, HostController>();
086:
087: // Regexp host
088: private ArrayList<HostConfig> _hostRegexpList = new ArrayList<HostConfig>();
089:
090: // List of default webApp configurations
091: private ArrayList<WebAppConfig> _webAppDefaultList = new ArrayList<WebAppConfig>();
092:
093: // List of default ear configurations
094: private ArrayList<EarConfig> _earDefaultList = new ArrayList<EarConfig>();
095:
096: // The configure exception
097: private Throwable _configException;
098:
099: // lifecycle
100: private final Lifecycle _lifecycle = new Lifecycle();
101:
102: /**
103: * Creates the webApp with its environment loader.
104: */
105: public HostContainer() {
106: _classLoader = new EnvironmentClassLoader();
107:
108: _rootDir = Vfs.lookup();
109: }
110:
111: /**
112: * Gets the environment class loader.
113: */
114: public ClassLoader getClassLoader() {
115: return _classLoader;
116: }
117:
118: /**
119: * Gets the environment class loader.
120: */
121: public void setClassLoader(EnvironmentClassLoader classLoader) {
122: _classLoader = classLoader;
123: }
124:
125: /**
126: * Sets the URL for the default server.
127: */
128: public void setURL(String url) {
129: _url = url;
130: }
131:
132: /**
133: * Gets the URL for the default server.
134: */
135: public String getURL() {
136: return _url;
137: }
138:
139: /**
140: * Sets the dispatch server.
141: */
142: public void setDispatchServer(DispatchServer server) {
143: _dispatchServer = server;
144: }
145:
146: /**
147: * Gets the dispatch server.
148: */
149: public DispatchServer getDispatchServer() {
150: return _dispatchServer;
151: }
152:
153: /**
154: * Gets the root directory.
155: */
156: public Path getRootDirectory() {
157: return _rootDir;
158: }
159:
160: /**
161: * Sets the root directory.
162: */
163: public void setRootDirectory(Path path) {
164: _rootDir = path;
165: }
166:
167: /**
168: * Sets the root directory (obsolete).
169: * @deprecated
170: */
171: public void setRootDir(Path path) {
172: setRootDirectory(path);
173: }
174:
175: /**
176: * Adds a host default
177: */
178: public void addHostDefault(HostConfig init) {
179: _hostDefaultList.add(init);
180: }
181:
182: /**
183: * Returns the list of host defaults
184: */
185: public ArrayList<HostConfig> getHostDefaultList() {
186: return _hostDefaultList;
187: }
188:
189: /**
190: * Creates a host deploy
191: */
192: public HostExpandDeployGenerator createHostDeploy() {
193: return new HostExpandDeployGenerator(_hostDeploy, this );
194: }
195:
196: /**
197: * Adds a host deploy
198: */
199: public void addHostDeploy(HostExpandDeployGenerator hostDeploy) {
200: _hostDeploy.add(hostDeploy);
201: }
202:
203: /**
204: * Adds a host.
205: */
206: public void addHost(HostConfig hostConfig) throws Exception {
207: if (hostConfig.getRegexp() != null) {
208: _hostDeploy.add(new HostRegexpDeployGenerator(_hostDeploy,
209: this , hostConfig));
210: return;
211: }
212:
213: HostSingleDeployGenerator deploy;
214: deploy = new HostSingleDeployGenerator(_hostDeploy, this ,
215: hostConfig);
216:
217: _hostDeploy.add(deploy);
218: }
219:
220: /**
221: * Adds a web-app default
222: */
223: public void addWebAppDefault(WebAppConfig init) {
224: _webAppDefaultList.add(init);
225: }
226:
227: /**
228: * Returns the list of web-app defaults
229: */
230: public ArrayList<WebAppConfig> getWebAppDefaultList() {
231: return _webAppDefaultList;
232: }
233:
234: /**
235: * Adds an ear default
236: */
237: public void addEarDefault(EarConfig init) {
238: _earDefaultList.add(init);
239: }
240:
241: /**
242: * Returns the list of ear defaults
243: */
244: public ArrayList<EarConfig> getEarDefaultList() {
245: return _earDefaultList;
246: }
247:
248: /**
249: * Adds rewrite-dispatch.
250: */
251: public RewriteDispatch createRewriteDispatch() {
252: if (_rewriteDispatch == null) {
253: _rewriteDispatch = new RewriteDispatch(getErrorWebApp());
254: }
255:
256: return _rewriteDispatch;
257: }
258:
259: /**
260: * Clears the cache.
261: */
262: public void clearCache() {
263: _hostMap.clear();
264: _dispatchServer.clearCache();
265: }
266:
267: /**
268: * Creates the invocation.
269: */
270: public Invocation buildInvocation(Invocation invocation)
271: throws Throwable {
272: String rawHost = invocation.getHost();
273: int rawPort = invocation.getPort();
274:
275: String hostName;
276:
277: if (rawHost == null)
278: hostName = "";
279: else
280: hostName = DomainName.fromAscii(rawHost);
281:
282: invocation.setHostName(hostName);
283:
284: boolean isAlwaysModified;
285:
286: Host host = getHost(hostName, rawPort);
287:
288: if (host != null) {
289: invocation = host.buildInvocation(invocation);
290: isAlwaysModified = false;
291: } else {
292: FilterChain chain = new ErrorFilterChain(404);
293: invocation.setFilterChain(chain);
294: invocation.setWebApp(getErrorWebApp());
295: isAlwaysModified = true;
296: }
297:
298: if (_rewriteDispatch != null) {
299: String url;
300:
301: if (invocation.isSecure())
302: url = "https://" + hostName + invocation.getURI();
303: else
304: url = "http://" + hostName + invocation.getURI();
305:
306: String queryString = invocation.getQueryString();
307:
308: FilterChain chain = invocation.getFilterChain();
309: FilterChain rewriteChain = _rewriteDispatch.map(url,
310: queryString, chain);
311:
312: if (rewriteChain != chain) {
313: Server server = (Server) _dispatchServer;
314: WebApp webApp = server.getDefaultWebApp();
315: invocation.setWebApp(webApp);
316:
317: if (webApp != null)
318: rewriteChain = new WebAppFilterChain(rewriteChain,
319: webApp);
320:
321: invocation.setFilterChain(rewriteChain);
322: isAlwaysModified = false;
323: }
324: }
325:
326: if (isAlwaysModified)
327: invocation.setDependency(AlwaysModified.create());
328:
329: return invocation;
330: }
331:
332: public ArrayList<HostController> getHostList() {
333: return _hostDeploy.getControllers();
334: }
335:
336: /**
337: * Returns the matching host.
338: */
339: public Host getHost(String hostName, int port) {
340: try {
341: HostController controller = findHost(hostName, port);
342:
343: if (controller != null)
344: return controller.request();
345: else
346: return null;
347: } catch (Throwable e) {
348: log.log(Level.WARNING, e.toString(), e);
349:
350: return null;
351: }
352: }
353:
354: /**
355: * Finds the best matching host entry.
356: */
357: private HostController findHost(String rawHost, int rawPort)
358: throws Exception {
359: if (rawHost == null)
360: rawHost = "";
361:
362: int p = rawHost.indexOf(':');
363:
364: String shortHost = rawHost;
365:
366: if (p > 0)
367: shortHost = rawHost.substring(0, p);
368:
369: String fullHost = shortHost + ':' + rawPort;
370:
371: HostController hostController = null;
372:
373: synchronized (_hostMap) {
374: hostController = _hostMap.get(fullHost);
375:
376: if (hostController != null && !hostController.isDestroyed())
377: return hostController;
378: }
379:
380: if (hostController == null || hostController.isDestroyed())
381: hostController = _hostMap.get(shortHost);
382:
383: if (hostController == null || hostController.isDestroyed())
384: hostController = findHostController(fullHost);
385:
386: if (hostController == null || hostController.isDestroyed())
387: hostController = findHostController(shortHost);
388:
389: if (hostController == null || hostController.isDestroyed())
390: hostController = findHostController("");
391:
392: synchronized (_hostMap) {
393: if (hostController != null && !hostController.isDestroyed())
394: _hostMap.put(fullHost, hostController);
395: else {
396: hostController = null;
397: _hostMap.remove(fullHost);
398: }
399: }
400:
401: return hostController;
402: }
403:
404: /**
405: * Returns the HostController based on a host name. The canonical name
406: * and the host aliases are tested for the match.
407: *
408: * @param hostName name to match on
409: * @return the host entry or null if none are found.
410: */
411: private HostController findHostController(String hostName)
412: throws Exception {
413: return _hostDeploy.findController(hostName);
414: }
415:
416: /**
417: * Returns the error webApp during startup.
418: */
419: public WebApp getErrorWebApp() {
420: if (_errorWebApp == null && _classLoader != null
421: && !_classLoader.isModified()) {
422: Thread thread = Thread.currentThread();
423: ClassLoader loader = thread.getContextClassLoader();
424: try {
425: thread.setContextClassLoader(_classLoader);
426:
427: _errorWebApp = new WebApp(getRootDirectory().lookup(
428: "caucho-host-error"));
429: _errorWebApp.init();
430: _errorWebApp.start();
431: } catch (Throwable e) {
432: log.log(Level.WARNING, e.toString(), e);
433: } finally {
434: thread.setContextClassLoader(loader);
435: }
436: }
437:
438: return _errorWebApp;
439: }
440:
441: /**
442: * Starts the container.
443: */
444: public void start() {
445: if (!_lifecycle.toStarting())
446: return;
447:
448: _classLoader.start();
449:
450: _lifecycle.toActive();
451:
452: _hostDeploy.start();
453: }
454:
455: /**
456: * Stops the container.
457: */
458: public void stop() {
459: if (!_lifecycle.toStop())
460: return;
461:
462: _hostDeploy.stop();
463:
464: _classLoader.stop();
465: }
466:
467: /**
468: * Closes the container.
469: */
470: public void destroy() {
471: stop();
472:
473: if (!_lifecycle.toDestroy())
474: return;
475:
476: _hostDeploy.destroy();
477:
478: _classLoader.destroy();
479: }
480: }
|