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.hmux;
031:
032: import com.caucho.log.Log;
033: import com.caucho.server.cluster.Cluster;
034: import com.caucho.server.cluster.ClusterPort;
035: import com.caucho.server.cluster.ClusterServer;
036: import com.caucho.server.cluster.Server;
037: import com.caucho.server.host.Host;
038: import com.caucho.server.webapp.WebApp;
039: import com.caucho.server.webapp.WebAppController;
040: import com.caucho.util.*;
041: import com.caucho.vfs.ReadStream;
042: import com.caucho.vfs.WriteStream;
043:
044: import java.io.IOException;
045: import java.util.ArrayList;
046: import java.util.logging.Level;
047: import java.util.logging.Logger;
048:
049: /**
050: * Handles the filter mapping (config) requests from a remote dispatcher.
051: */
052: public class HmuxDispatchRequest {
053: private static final Logger log = Log
054: .open(HmuxDispatchRequest.class);
055:
056: // other, specialized protocols
057: public static final int HMUX_HOST = 'h';
058: public static final int HMUX_QUERY_ALL = 'q';
059: public static final int HMUX_QUERY_URL = 'r';
060: public static final int HMUX_QUERY_SERVER = 's';
061: public static final int HMUX_WEB_APP = 'a';
062: public static final int HMUX_MATCH = 'm';
063: public static final int HMUX_IGNORE = 'i';
064: public static final int HMUX_ETAG = 'e';
065: public static final int HMUX_NO_CHANGE = 'n';
066: public static final int HMUX_CLUSTER = 'c';
067: public static final int HMUX_SRUN = 's';
068: public static final int HMUX_SRUN_BACKUP = 'b';
069: public static final int HMUX_SRUN_SSL = 'e';
070: public static final int HMUX_UNAVAILABLE = 'u';
071: public static final int HMUX_WEB_APP_UNAVAILABLE = 'U';
072:
073: private CharBuffer _cb = new CharBuffer();
074:
075: private HmuxRequest _request;
076: private Server _server;
077:
078: private int _srunIndex;
079:
080: public HmuxDispatchRequest(HmuxRequest request) {
081: _request = request;
082:
083: _server = (Server) request.getDispatchServer();
084: }
085:
086: /**
087: * Handles a new request. Initializes the protocol handler and
088: * the request streams.
089: *
090: * <p>Note: ClientDisconnectException must be rethrown to
091: * the caller.
092: */
093: public boolean handleRequest(ReadStream is, WriteStream os)
094: throws IOException {
095: CharBuffer cb = _cb;
096: boolean isLoggable = log.isLoggable(Level.FINE);
097: int code;
098: int len;
099: String host = "";
100: String etag = null;
101:
102: while (true) {
103: code = is.read();
104:
105: switch (code) {
106: case -1:
107: if (isLoggable)
108: log.fine(dbgId() + "end of file");
109: return false;
110:
111: case HmuxRequest.HMUX_QUIT:
112: if (isLoggable)
113: log
114: .fine(dbgId() + (char) code
115: + ": end of request");
116: return true;
117:
118: case HmuxRequest.HMUX_EXIT:
119: if (isLoggable)
120: log.fine(dbgId() + (char) code + ": end of socket");
121:
122: return false;
123:
124: case HMUX_ETAG:
125: len = (is.read() << 8) + is.read();
126: _cb.clear();
127: is.readAll(_cb, len);
128: etag = _cb.toString();
129:
130: if (isLoggable)
131: log.fine(dbgId() + "etag: " + etag);
132: break;
133:
134: case HMUX_HOST:
135: len = (is.read() << 8) + is.read();
136: _cb.clear();
137: is.readAll(_cb, len);
138: host = _cb.toString();
139:
140: if (isLoggable)
141: log.fine(dbgId() + "host: " + host);
142: break;
143:
144: case HMUX_QUERY_ALL:
145: len = (is.read() << 8) + is.read();
146: _cb.clear();
147: is.readAll(_cb, len);
148:
149: if (isLoggable)
150: log.fine(dbgId() + "query: " + _cb);
151:
152: queryAll(os, host, _cb.toString(), etag);
153: break;
154:
155: /*
156: case HMUX_QUERY_SERVER:
157: len = (is.read() << 8) + is.read();
158: _cb.clear();
159: is.readAll(_cb, len);
160:
161: if (isLoggable)
162: log.fine(dbgId() + "query-server: " + _cb);
163:
164: queryCluster(os, host, _cb.toString());
165: break;
166: */
167:
168: default:
169: len = (is.read() << 8) + is.read();
170:
171: if (isLoggable)
172: log.fine(dbgId() + (char) code + " " + len
173: + " (dispatch)");
174: is.skip(len);
175: break;
176: }
177: }
178:
179: // _filter.setClientClosed(true);
180:
181: // return false;
182: }
183:
184: /**
185: * Returns the url.
186: */
187: private void queryAll(WriteStream os, String hostName, String url,
188: String etag) throws IOException {
189: int channel = 2;
190: boolean isLoggable = log.isLoggable(Level.FINE);
191:
192: os.write(HmuxRequest.HMUX_CHANNEL);
193: os.write(channel >> 8);
194: os.write(channel);
195:
196: Host host = _server.getHost(hostName, 80);
197: if (host == null) {
198: writeString(os, HmuxRequest.HMUX_HEADER, "check-interval");
199: writeString(
200: os,
201: HmuxRequest.HMUX_STRING,
202: String
203: .valueOf(_server
204: .getDependencyCheckInterval() / 1000));
205:
206: if (isLoggable)
207: log
208: .fine(dbgId() + "host '" + host
209: + "' not configured");
210: return;
211: } else if (!host.isActive()) {
212: writeString(os, HMUX_UNAVAILABLE, "");
213:
214: if (isLoggable)
215: log.fine(dbgId() + "host '" + host + "' not active");
216: return;
217: }
218:
219: if (host.getConfigETag() == null)
220: sendQuery(null, host, hostName, url);
221:
222: if (etag == null) {
223: } else if (etag.equals(host.getConfigETag())) {
224: if (isLoggable)
225: log.fine(dbgId() + "host '" + host + "' no change");
226:
227: writeString(os, HMUX_NO_CHANGE, "");
228: return;
229: } else if (etag.equals("h-" + host.getHostName())) {
230: if (isLoggable) {
231: log.fine(dbgId() + "host alias '" + hostName + " -> '"
232: + host + "' no change");
233: }
234:
235: writeString(os, HMUX_NO_CHANGE, "");
236: return;
237: } else {
238: if (isLoggable)
239: log.fine(dbgId() + "host '" + host + "' changed");
240: }
241:
242: sendQuery(os, host, hostName, url);
243: }
244:
245: /**
246: * Writes the host data, returning the crc
247: */
248: private void sendQuery(WriteStream os, Host host, String hostName,
249: String url) throws IOException {
250: boolean isLoggable = log.isLoggable(Level.FINE);
251:
252: long crc64 = 0;
253:
254: if (!Alarm.isTest())
255: crc64 = Crc64.generate(crc64,
256: com.caucho.Version.FULL_VERSION);
257:
258: queryServer(os);
259:
260: writeString(os, HMUX_HOST, host.getHostName());
261:
262: if (hostName.equals(host.getHostName())) {
263: crc64 = queryCluster(os, host, crc64);
264:
265: WebAppController controller = host.findByURI(url);
266: if (controller != null) {
267: try {
268: controller.request();
269: } catch (Throwable e) {
270: log.log(Level.WARNING, e.toString(), e);
271: }
272: }
273:
274: ArrayList<WebAppController> appList = host.getWebAppList();
275:
276: for (int i = 0; i < appList.size(); i++) {
277: WebAppController appEntry = appList.get(i);
278:
279: if (appEntry.getParent() != null
280: && appEntry.getParent().isDynamicDeploy()) {
281: continue;
282: }
283:
284: writeString(os, HMUX_WEB_APP, appEntry.getContextPath());
285: if (isLoggable)
286: log.fine(dbgId() + "web-app '"
287: + appEntry.getContextPath() + "'");
288:
289: crc64 = Crc64
290: .generate(crc64, appEntry.getContextPath());
291:
292: WebApp app = appEntry.getWebApp();
293:
294: if (appEntry.isDynamicDeploy()) {
295: writeString(os, HMUX_MATCH, "/*");
296:
297: crc64 = Crc64.generate(crc64, "/*");
298:
299: if (isLoggable)
300: log.fine(dbgId() + "dynamic '"
301: + appEntry.getContextPath() + "'");
302: } else if (app == null || !app.isActive()) {
303: if (isLoggable)
304: log.fine(dbgId() + "not active '"
305: + appEntry.getContextPath() + "'");
306:
307: writeString(os, HMUX_WEB_APP_UNAVAILABLE, "");
308: } else {
309: if (isLoggable)
310: log.fine(dbgId() + "active '"
311: + appEntry.getContextPath() + "'");
312: ArrayList<String> patternList = app
313: .getServletMappingPatterns();
314:
315: for (int j = 0; patternList != null
316: && j < patternList.size(); j++) {
317: String pattern = patternList.get(j);
318:
319: writeString(os, HMUX_MATCH, pattern);
320:
321: crc64 = Crc64.generate(crc64, pattern);
322: }
323:
324: patternList = app.getServletIgnoreMappingPatterns();
325:
326: for (int j = 0; patternList != null
327: && j < patternList.size(); j++) {
328: String pattern = patternList.get(j);
329:
330: writeString(os, HMUX_IGNORE, pattern);
331:
332: crc64 = Crc64.generate(crc64, "i");
333: crc64 = Crc64.generate(crc64, pattern);
334: }
335: }
336: }
337:
338: CharBuffer cb = new CharBuffer();
339: Base64.encode(cb, crc64);
340: String newETag = cb.close();
341: host.setConfigETag(newETag);
342:
343: writeString(os, HMUX_ETAG, host.getConfigETag());
344: } else {
345: // aliased hosts use the host name as the etag
346: writeString(os, HMUX_ETAG, "h-" + host.getHostName());
347: }
348: }
349:
350: /**
351: * Queries the cluster.
352: */
353: private long queryCluster(WriteStream os, Host host, long crc64)
354: throws IOException {
355: /*
356: int channel = 2;
357:
358: os.write(HmuxRequest.HMUX_CHANNEL);
359: os.write(channel >> 8);
360: os.write(channel);
361: */
362:
363: Cluster cluster = host.getCluster();
364:
365: if (cluster == null)
366: return 0;
367:
368: writeString(os, HMUX_CLUSTER, cluster.getId());
369:
370: crc64 = Crc64.generate(crc64, cluster.getId());
371:
372: ClusterServer[] servers = cluster.getServerList();
373: if (servers.length > 0) {
374: ClusterServer server = servers[0];
375:
376: writeString(os, HmuxRequest.HMUX_HEADER, "live-time");
377: writeString(os, HmuxRequest.HMUX_STRING, ""
378: + (server.getLoadBalanceIdleTime() / 1000));
379:
380: writeString(os, HmuxRequest.HMUX_HEADER, "dead-time");
381: writeString(os, HmuxRequest.HMUX_STRING, ""
382: + (server.getLoadBalanceRecoverTime() / 1000));
383:
384: writeString(os, HmuxRequest.HMUX_HEADER, "read-timeout");
385: writeString(os, HmuxRequest.HMUX_STRING, ""
386: + (server.getLoadBalanceSocketTimeout() / 1000));
387: }
388:
389: for (int i = 0; i < servers.length; i++) {
390: ClusterServer server = servers[i];
391:
392: if (server != null && server.getClusterPort() != null) {
393: ClusterPort port = server.getClusterPort();
394:
395: String srunHost = port.getAddress() + ":"
396: + port.getPort();
397:
398: /*
399: if (server.isBackup())
400: writeString(os, HMUX_SRUN_BACKUP, srunHost);
401: else
402: */
403: if (server.isSSL())
404: writeString(os, HMUX_SRUN_SSL, srunHost);
405: else
406: writeString(os, HMUX_SRUN, srunHost);
407:
408: crc64 = Crc64.generate(crc64, srunHost);
409: }
410: }
411:
412: return crc64;
413: }
414:
415: /**
416: * Queries the cluster.
417: */
418: private void queryServer(WriteStream os) throws IOException {
419: writeString(os, HmuxRequest.HMUX_HEADER, "check-interval");
420: writeString(os, HmuxRequest.HMUX_STRING, String.valueOf(_server
421: .getDependencyCheckInterval() / 1000));
422:
423: writeString(os, HmuxRequest.HMUX_HEADER, "cookie");
424: writeString(os, HmuxRequest.HMUX_STRING, _server
425: .getSessionCookie());
426: writeString(os, HmuxRequest.HMUX_HEADER, "ssl-cookie");
427: writeString(os, HmuxRequest.HMUX_STRING, _server
428: .getSSLSessionCookie());
429: writeString(os, HmuxRequest.HMUX_HEADER, "session-url-prefix");
430: writeString(os, HmuxRequest.HMUX_STRING, _server
431: .getSessionURLPrefix());
432: writeString(os, HmuxRequest.HMUX_HEADER,
433: "alt-session-url-prefix");
434: writeString(os, HmuxRequest.HMUX_STRING, _server
435: .getAlternateSessionURLPrefix());
436:
437: if (_server.getConnectionErrorPage() != null) {
438: writeString(os, HmuxRequest.HMUX_HEADER,
439: "connection-error-page");
440: writeString(os, HmuxRequest.HMUX_STRING, _server
441: .getConnectionErrorPage());
442: }
443: }
444:
445: void writeString(WriteStream os, int code, String value)
446: throws IOException {
447: if (os == null)
448: return;
449:
450: if (value == null)
451: value = "";
452:
453: int len = value.length();
454:
455: os.write(code);
456: os.write(len >> 8);
457: os.write(len);
458: os.print(value);
459:
460: if (log.isLoggable(Level.FINE))
461: log.fine(dbgId() + (char) code + " " + value);
462: }
463:
464: void writeString(WriteStream os, int code, CharBuffer value)
465: throws IOException {
466: if (os == null)
467: return;
468:
469: int len = value.length();
470:
471: os.write(code);
472: os.write(len >> 8);
473: os.write(len);
474: os.print(value);
475:
476: if (log.isLoggable(Level.FINE))
477: log.fine(dbgId() + (char) code + " " + value);
478: }
479:
480: private String dbgId() {
481: return _request.dbgId();
482: }
483: }
|