001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.coyote.ajp;
019:
020: import java.net.InetAddress;
021: import java.net.Socket;
022: import java.net.URLEncoder;
023: import java.util.Hashtable;
024: import java.util.Iterator;
025: import java.util.concurrent.ConcurrentLinkedQueue;
026: import java.util.concurrent.Executor;
027: import java.util.concurrent.atomic.AtomicInteger;
028: import java.util.concurrent.atomic.AtomicLong;
029:
030: import javax.management.MBeanRegistration;
031: import javax.management.MBeanServer;
032: import javax.management.ObjectName;
033:
034: import org.apache.coyote.ActionCode;
035: import org.apache.coyote.ActionHook;
036: import org.apache.coyote.Adapter;
037: import org.apache.coyote.ProtocolHandler;
038: import org.apache.coyote.RequestGroupInfo;
039: import org.apache.coyote.RequestInfo;
040: import org.apache.tomcat.util.modeler.Registry;
041: import org.apache.tomcat.util.net.JIoEndpoint;
042: import org.apache.tomcat.util.net.JIoEndpoint.Handler;
043: import org.apache.tomcat.util.res.StringManager;
044:
045: /**
046: * Abstract the protocol implementation, including threading, etc.
047: * Processor is single threaded and specific to stream-based protocols,
048: * will not fit Jk protocols like JNI.
049: *
050: * @author Remy Maucherat
051: * @author Costin Manolache
052: */
053: public class AjpProtocol implements ProtocolHandler, MBeanRegistration {
054:
055: protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
056: .getLog(AjpProtocol.class);
057:
058: /**
059: * The string manager for this package.
060: */
061: protected static StringManager sm = StringManager
062: .getManager(Constants.Package);
063:
064: // ------------------------------------------------------------ Constructor
065:
066: public AjpProtocol() {
067: cHandler = new AjpConnectionHandler(this );
068: setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
069: setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
070: //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
071: setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
072: }
073:
074: // ----------------------------------------------------- Instance Variables
075:
076: protected ObjectName tpOname;
077:
078: protected ObjectName rgOname;
079:
080: /**
081: * Associated java.io endpoint.
082: */
083: protected JIoEndpoint endpoint = new JIoEndpoint();
084:
085: /**
086: * Configuration attributes.
087: */
088: protected Hashtable attributes = new Hashtable();
089:
090: /**
091: * Adapter which will process the requests recieved by this endpoint.
092: */
093: private Adapter adapter;
094:
095: /**
096: * Connection handler for AJP.
097: */
098: private AjpConnectionHandler cHandler;
099:
100: // --------------------------------------------------------- Public Methods
101:
102: /**
103: * Pass config info
104: */
105: public void setAttribute(String name, Object value) {
106: if (log.isTraceEnabled()) {
107: log.trace(sm.getString("ajpprotocol.setattribute", name,
108: value));
109: }
110: attributes.put(name, value);
111: }
112:
113: public Object getAttribute(String key) {
114: if (log.isTraceEnabled()) {
115: log.trace(sm.getString("ajpprotocol.getattribute", key));
116: }
117: return attributes.get(key);
118: }
119:
120: public Iterator getAttributeNames() {
121: return attributes.keySet().iterator();
122: }
123:
124: /**
125: * Set a property.
126: */
127: public void setProperty(String name, String value) {
128: setAttribute(name, value);
129: }
130:
131: /**
132: * Get a property
133: */
134: public String getProperty(String name) {
135: return (String) getAttribute(name);
136: }
137:
138: /**
139: * The adapter, used to call the connector
140: */
141: public void setAdapter(Adapter adapter) {
142: this .adapter = adapter;
143: }
144:
145: public Adapter getAdapter() {
146: return adapter;
147: }
148:
149: /** Start the protocol
150: */
151: public void init() throws Exception {
152: endpoint.setName(getName());
153: endpoint.setHandler(cHandler);
154:
155: try {
156: endpoint.init();
157: } catch (Exception ex) {
158: log.error(sm.getString("ajpprotocol.endpoint.initerror"),
159: ex);
160: throw ex;
161: }
162: if (log.isInfoEnabled()) {
163: log.info(sm.getString("ajpprotocol.init", getName()));
164: }
165: }
166:
167: public void start() throws Exception {
168: if (this .domain != null) {
169: try {
170: tpOname = new ObjectName(domain + ":"
171: + "type=ThreadPool,name=" + getName());
172: Registry.getRegistry(null, null).registerComponent(
173: endpoint, tpOname, null);
174: } catch (Exception e) {
175: log.error("Can't register threadpool");
176: }
177: rgOname = new ObjectName(domain
178: + ":type=GlobalRequestProcessor,name=" + getName());
179: Registry.getRegistry(null, null).registerComponent(
180: cHandler.global, rgOname, null);
181: }
182:
183: try {
184: endpoint.start();
185: } catch (Exception ex) {
186: log.error(sm.getString("ajpprotocol.endpoint.starterror"),
187: ex);
188: throw ex;
189: }
190: if (log.isInfoEnabled())
191: log.info(sm.getString("ajpprotocol.start", getName()));
192: }
193:
194: public void pause() throws Exception {
195: try {
196: endpoint.pause();
197: } catch (Exception ex) {
198: log.error(sm.getString("ajpprotocol.endpoint.pauseerror"),
199: ex);
200: throw ex;
201: }
202: if (log.isInfoEnabled())
203: log.info(sm.getString("ajpprotocol.pause", getName()));
204: }
205:
206: public void resume() throws Exception {
207: try {
208: endpoint.resume();
209: } catch (Exception ex) {
210: log.error(sm.getString("ajpprotocol.endpoint.resumeerror"),
211: ex);
212: throw ex;
213: }
214: if (log.isInfoEnabled())
215: log.info(sm.getString("ajpprotocol.resume", getName()));
216: }
217:
218: public void destroy() throws Exception {
219: if (log.isInfoEnabled())
220: log.info(sm.getString("ajpprotocol.stop", getName()));
221: endpoint.destroy();
222: if (tpOname != null)
223: Registry.getRegistry(null, null).unregisterComponent(
224: tpOname);
225: if (rgOname != null)
226: Registry.getRegistry(null, null).unregisterComponent(
227: rgOname);
228: }
229:
230: // *
231: public String getName() {
232: String encodedAddr = "";
233: if (getAddress() != null) {
234: encodedAddr = "" + getAddress();
235: if (encodedAddr.startsWith("/"))
236: encodedAddr = encodedAddr.substring(1);
237: encodedAddr = URLEncoder.encode(encodedAddr) + "-";
238: }
239: return ("ajp-" + encodedAddr + endpoint.getPort());
240: }
241:
242: /**
243: * Processor cache.
244: */
245: protected int processorCache = -1;
246:
247: public int getProcessorCache() {
248: return this .processorCache;
249: }
250:
251: public void setProcessorCache(int processorCache) {
252: this .processorCache = processorCache;
253: }
254:
255: public Executor getExecutor() {
256: return endpoint.getExecutor();
257: }
258:
259: public void setExecutor(Executor executor) {
260: endpoint.setExecutor(executor);
261: }
262:
263: public int getMaxThreads() {
264: return endpoint.getMaxThreads();
265: }
266:
267: public void setMaxThreads(int maxThreads) {
268: endpoint.setMaxThreads(maxThreads);
269: }
270:
271: public int getThreadPriority() {
272: return endpoint.getThreadPriority();
273: }
274:
275: public void setThreadPriority(int threadPriority) {
276: endpoint.setThreadPriority(threadPriority);
277: }
278:
279: public int getBacklog() {
280: return endpoint.getBacklog();
281: }
282:
283: public void setBacklog(int backlog) {
284: endpoint.setBacklog(backlog);
285: }
286:
287: public int getPort() {
288: return endpoint.getPort();
289: }
290:
291: public void setPort(int port) {
292: endpoint.setPort(port);
293: }
294:
295: public InetAddress getAddress() {
296: return endpoint.getAddress();
297: }
298:
299: public void setAddress(InetAddress ia) {
300: endpoint.setAddress(ia);
301: }
302:
303: public boolean getTcpNoDelay() {
304: return endpoint.getTcpNoDelay();
305: }
306:
307: public void setTcpNoDelay(boolean tcpNoDelay) {
308: endpoint.setTcpNoDelay(tcpNoDelay);
309: }
310:
311: public int getSoLinger() {
312: return endpoint.getSoLinger();
313: }
314:
315: public void setSoLinger(int soLinger) {
316: endpoint.setSoLinger(soLinger);
317: }
318:
319: public int getSoTimeout() {
320: return endpoint.getSoTimeout();
321: }
322:
323: public void setSoTimeout(int soTimeout) {
324: endpoint.setSoTimeout(soTimeout);
325: }
326:
327: /**
328: * Should authentication be done in the native webserver layer,
329: * or in the Servlet container ?
330: */
331: protected boolean tomcatAuthentication = true;
332:
333: public boolean getTomcatAuthentication() {
334: return tomcatAuthentication;
335: }
336:
337: public void setTomcatAuthentication(boolean tomcatAuthentication) {
338: this .tomcatAuthentication = tomcatAuthentication;
339: }
340:
341: /**
342: * Required secret.
343: */
344: protected String requiredSecret = null;
345:
346: public void setRequiredSecret(String requiredSecret) {
347: this .requiredSecret = requiredSecret;
348: }
349:
350: /**
351: * AJP packet size.
352: */
353: protected int packetSize = Constants.MAX_PACKET_SIZE;
354:
355: public int getPacketSize() {
356: return packetSize;
357: }
358:
359: public void setPacketSize(int packetSize) {
360: this .packetSize = packetSize;
361: }
362:
363: /**
364: * The number of seconds Tomcat will wait for a subsequent request
365: * before closing the connection.
366: */
367: protected int keepAliveTimeout = -1;
368:
369: public int getKeepAliveTimeout() {
370: return keepAliveTimeout;
371: }
372:
373: public void setKeepAliveTimeout(int timeout) {
374: keepAliveTimeout = timeout;
375: }
376:
377: // -------------------------------------- AjpConnectionHandler Inner Class
378:
379: protected static class AjpConnectionHandler implements Handler {
380:
381: protected AjpProtocol proto;
382: protected AtomicLong registerCount = new AtomicLong(0);
383: protected RequestGroupInfo global = new RequestGroupInfo();
384:
385: protected ConcurrentLinkedQueue<AjpProcessor> recycledProcessors = new ConcurrentLinkedQueue<AjpProcessor>() {
386: protected AtomicInteger size = new AtomicInteger(0);
387:
388: public boolean offer(AjpProcessor processor) {
389: boolean offer = (proto.processorCache == -1) ? true
390: : (size.get() < proto.processorCache);
391: //avoid over growing our cache or add after we have stopped
392: boolean result = false;
393: if (offer) {
394: result = super .offer(processor);
395: if (result) {
396: size.incrementAndGet();
397: }
398: }
399: if (!result)
400: unregister(processor);
401: return result;
402: }
403:
404: public AjpProcessor poll() {
405: AjpProcessor result = super .poll();
406: if (result != null) {
407: size.decrementAndGet();
408: }
409: return result;
410: }
411:
412: public void clear() {
413: AjpProcessor next = poll();
414: while (next != null) {
415: unregister(next);
416: next = poll();
417: }
418: super .clear();
419: size.set(0);
420: }
421: };
422:
423: public AjpConnectionHandler(AjpProtocol proto) {
424: this .proto = proto;
425: }
426:
427: public boolean process(Socket socket) {
428: AjpProcessor processor = recycledProcessors.poll();
429: try {
430:
431: if (processor == null) {
432: processor = createProcessor();
433: }
434:
435: if (processor instanceof ActionHook) {
436: ((ActionHook) processor).action(
437: ActionCode.ACTION_START, null);
438: }
439:
440: return processor.process(socket);
441:
442: } catch (java.net.SocketException e) {
443: // SocketExceptions are normal
444: AjpProtocol.log
445: .debug(
446: sm
447: .getString("ajpprotocol.proto.socketexception.debug"),
448: e);
449: } catch (java.io.IOException e) {
450: // IOExceptions are normal
451: AjpProtocol.log
452: .debug(
453: sm
454: .getString("ajpprotocol.proto.ioexception.debug"),
455: e);
456: }
457: // Future developers: if you discover any other
458: // rare-but-nonfatal exceptions, catch them here, and log as
459: // above.
460: catch (Throwable e) {
461: // any other exception or error is odd. Here we log it
462: // with "ERROR" level, so it will show up even on
463: // less-than-verbose logs.
464: AjpProtocol.log.error(sm
465: .getString("ajpprotocol.proto.error"), e);
466: } finally {
467: if (processor instanceof ActionHook) {
468: ((ActionHook) processor).action(
469: ActionCode.ACTION_STOP, null);
470: }
471: recycledProcessors.offer(processor);
472: }
473: return false;
474: }
475:
476: protected AjpProcessor createProcessor() {
477: AjpProcessor processor = new AjpProcessor(proto.packetSize,
478: proto.endpoint);
479: processor.setAdapter(proto.adapter);
480: processor
481: .setTomcatAuthentication(proto.tomcatAuthentication);
482: processor.setRequiredSecret(proto.requiredSecret);
483: processor.setKeepAliveTimeout(proto.keepAliveTimeout);
484: register(processor);
485: return processor;
486: }
487:
488: protected void register(AjpProcessor processor) {
489: if (proto.getDomain() != null) {
490: synchronized (this ) {
491: try {
492: long count = registerCount.incrementAndGet();
493: RequestInfo rp = processor.getRequest()
494: .getRequestProcessor();
495: rp.setGlobalProcessor(global);
496: ObjectName rpName = new ObjectName(proto
497: .getDomain()
498: + ":type=RequestProcessor,worker="
499: + proto.getName()
500: + ",name=AjpRequest"
501: + count);
502: if (log.isDebugEnabled()) {
503: log.debug("Register " + rpName);
504: }
505: Registry.getRegistry(null, null)
506: .registerComponent(rp, rpName, null);
507: rp.setRpName(rpName);
508: } catch (Exception e) {
509: log.warn("Error registering request");
510: }
511: }
512: }
513: }
514:
515: protected void unregister(AjpProcessor processor) {
516: if (proto.getDomain() != null) {
517: synchronized (this ) {
518: try {
519: RequestInfo rp = processor.getRequest()
520: .getRequestProcessor();
521: rp.setGlobalProcessor(null);
522: ObjectName rpName = rp.getRpName();
523: if (log.isDebugEnabled()) {
524: log.debug("Unregister " + rpName);
525: }
526: Registry.getRegistry(null, null)
527: .unregisterComponent(rpName);
528: rp.setRpName(null);
529: } catch (Exception e) {
530: log.warn("Error unregistering request", e);
531: }
532: }
533: }
534: }
535:
536: }
537:
538: // -------------------- Various implementation classes --------------------
539:
540: protected String domain;
541: protected ObjectName oname;
542: protected MBeanServer mserver;
543:
544: public ObjectName getObjectName() {
545: return oname;
546: }
547:
548: public String getDomain() {
549: return domain;
550: }
551:
552: public ObjectName preRegister(MBeanServer server, ObjectName name)
553: throws Exception {
554: oname = name;
555: mserver = server;
556: domain = name.getDomain();
557: return name;
558: }
559:
560: public void postRegister(Boolean registrationDone) {
561: }
562:
563: public void preDeregister() throws Exception {
564: }
565:
566: public void postDeregister() {
567: }
568:
569: }
|