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