1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.coyote.ajp;
19
20 import java.net.InetAddress;
21 import java.net.URLEncoder;
22 import java.util.Hashtable;
23 import java.util.Iterator;
24 import java.util.concurrent.ConcurrentLinkedQueue;
25 import java.util.concurrent.Executor;
26 import java.util.concurrent.atomic.AtomicInteger;
27 import java.util.concurrent.atomic.AtomicLong;
28
29 import javax.management.MBeanRegistration;
30 import javax.management.MBeanServer;
31 import javax.management.ObjectName;
32
33 import org.apache.coyote.ActionCode;
34 import org.apache.coyote.ActionHook;
35 import org.apache.coyote.Adapter;
36 import org.apache.coyote.ProtocolHandler;
37 import org.apache.coyote.RequestGroupInfo;
38 import org.apache.coyote.RequestInfo;
39 import org.apache.tomcat.util.modeler.Registry;
40 import org.apache.tomcat.util.net.AprEndpoint;
41 import org.apache.tomcat.util.net.SocketStatus;
42 import org.apache.tomcat.util.net.AprEndpoint.Handler;
43 import org.apache.tomcat.util.res.StringManager;
44
45
46 /**
47 * Abstract the protocol implementation, including threading, etc.
48 * Processor is single threaded and specific to stream-based protocols,
49 * will not fit Jk protocols like JNI.
50 *
51 * @author Remy Maucherat
52 * @author Costin Manolache
53 */
54 public class AjpAprProtocol
55 implements ProtocolHandler, MBeanRegistration {
56
57
58 protected static org.apache.juli.logging.Log log =
59 org.apache.juli.logging.LogFactory.getLog(AjpAprProtocol.class);
60
61 /**
62 * The string manager for this package.
63 */
64 protected static StringManager sm =
65 StringManager.getManager(Constants.Package);
66
67
68 // ------------------------------------------------------------ Constructor
69
70
71 public AjpAprProtocol() {
72 cHandler = new AjpConnectionHandler(this);
73 setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
74 setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
75 //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
76 setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
77 }
78
79
80 // ----------------------------------------------------- Instance Variables
81
82
83 protected ObjectName tpOname;
84
85
86 protected ObjectName rgOname;
87
88
89 /**
90 * Associated APR endpoint.
91 */
92 protected AprEndpoint endpoint = new AprEndpoint();
93
94
95 /**
96 * Configuration attributes.
97 */
98 protected Hashtable attributes = new Hashtable();
99
100
101 /**
102 * Adapter which will process the requests recieved by this endpoint.
103 */
104 private Adapter adapter;
105
106
107 /**
108 * Connection handler for AJP.
109 */
110 private AjpConnectionHandler cHandler;
111
112
113 // --------------------------------------------------------- Public Methods
114
115
116 /**
117 * Pass config info
118 */
119 public void setAttribute(String name, Object value) {
120 if (log.isTraceEnabled()) {
121 log.trace(sm.getString("ajpprotocol.setattribute", name, value));
122 }
123 attributes.put(name, value);
124 }
125
126 public Object getAttribute(String key) {
127 if (log.isTraceEnabled()) {
128 log.trace(sm.getString("ajpprotocol.getattribute", key));
129 }
130 return attributes.get(key);
131 }
132
133
134 public Iterator getAttributeNames() {
135 return attributes.keySet().iterator();
136 }
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
147 public Adapter getAdapter() {
148 return adapter;
149 }
150
151
152 /** Start the protocol
153 */
154 public void init() throws Exception {
155 endpoint.setName(getName());
156 endpoint.setHandler(cHandler);
157 endpoint.setUseSendfile(false);
158
159 try {
160 endpoint.init();
161 } catch (Exception ex) {
162 log.error(sm.getString("ajpprotocol.endpoint.initerror"), ex);
163 throw ex;
164 }
165 if (log.isInfoEnabled()) {
166 log.info(sm.getString("ajpprotocol.init", getName()));
167 }
168 }
169
170
171 public void start() throws Exception {
172 if (this.domain != null ) {
173 try {
174 tpOname = new ObjectName
175 (domain + ":" + "type=ThreadPool,name=" + getName());
176 Registry.getRegistry(null, null)
177 .registerComponent(endpoint, tpOname, null );
178 } catch (Exception e) {
179 log.error("Can't register threadpool" );
180 }
181 rgOname = new ObjectName
182 (domain + ":type=GlobalRequestProcessor,name=" + getName());
183 Registry.getRegistry(null, null).registerComponent
184 (cHandler.global, rgOname, null);
185 }
186
187 try {
188 endpoint.start();
189 } catch (Exception ex) {
190 log.error(sm.getString("ajpprotocol.endpoint.starterror"), ex);
191 throw ex;
192 }
193 if (log.isInfoEnabled())
194 log.info(sm.getString("ajpprotocol.start", getName()));
195 }
196
197 public void pause() throws Exception {
198 try {
199 endpoint.pause();
200 } catch (Exception ex) {
201 log.error(sm.getString("ajpprotocol.endpoint.pauseerror"), 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"), ex);
213 throw ex;
214 }
215 if (log.isInfoEnabled())
216 log.info(sm.getString("ajpprotocol.resume", getName()));
217 }
218
219 public void destroy() throws Exception {
220 if (log.isInfoEnabled())
221 log.info(sm.getString("ajpprotocol.stop", getName()));
222 endpoint.destroy();
223 if (tpOname!=null)
224 Registry.getRegistry(null, null).unregisterComponent(tpOname);
225 if (rgOname != null)
226 Registry.getRegistry(null, null).unregisterComponent(rgOname);
227 }
228
229 // *
230 public String getName() {
231 String encodedAddr = "";
232 if (getAddress() != null) {
233 encodedAddr = "" + getAddress();
234 if (encodedAddr.startsWith("/"))
235 encodedAddr = encodedAddr.substring(1);
236 encodedAddr = URLEncoder.encode(encodedAddr) + "-";
237 }
238 return ("ajp-" + encodedAddr + endpoint.getPort());
239 }
240
241 /**
242 * Processor cache.
243 */
244 protected int processorCache = -1;
245 public int getProcessorCache() { return this.processorCache; }
246 public void setProcessorCache(int processorCache) { this.processorCache = processorCache; }
247
248 public Executor getExecutor() { return endpoint.getExecutor(); }
249 public void setExecutor(Executor executor) { endpoint.setExecutor(executor); }
250
251 public int getMaxThreads() { return endpoint.getMaxThreads(); }
252 public void setMaxThreads(int maxThreads) { endpoint.setMaxThreads(maxThreads); }
253
254 public int getThreadPriority() { return endpoint.getThreadPriority(); }
255 public void setThreadPriority(int threadPriority) { endpoint.setThreadPriority(threadPriority); }
256
257 public int getBacklog() { return endpoint.getBacklog(); }
258 public void setBacklog(int backlog) { endpoint.setBacklog(backlog); }
259
260 public int getPort() { return endpoint.getPort(); }
261 public void setPort(int port) { endpoint.setPort(port); }
262
263 public InetAddress getAddress() { return endpoint.getAddress(); }
264 public void setAddress(InetAddress ia) { endpoint.setAddress(ia); }
265
266 public boolean getTcpNoDelay() { return endpoint.getTcpNoDelay(); }
267 public void setTcpNoDelay(boolean tcpNoDelay) { endpoint.setTcpNoDelay(tcpNoDelay); }
268
269 public int getSoLinger() { return endpoint.getSoLinger(); }
270 public void setSoLinger(int soLinger) { endpoint.setSoLinger(soLinger); }
271
272 public int getSoTimeout() { return endpoint.getSoTimeout(); }
273 public void setSoTimeout(int soTimeout) { endpoint.setSoTimeout(soTimeout); }
274
275 /**
276 * Should authentication be done in the native webserver layer,
277 * or in the Servlet container ?
278 */
279 protected boolean tomcatAuthentication = true;
280 public boolean getTomcatAuthentication() { return tomcatAuthentication; }
281 public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
282
283 /**
284 * Required secret.
285 */
286 protected String requiredSecret = null;
287 public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
288
289 /**
290 * AJP packet size.
291 */
292 protected int packetSize = Constants.MAX_PACKET_SIZE;
293 public int getPacketSize() { return packetSize; }
294 public void setPacketSize(int packetSize) { this.packetSize = packetSize; }
295
296 /**
297 * The number of seconds Tomcat will wait for a subsequent request
298 * before closing the connection.
299 */
300 public int getKeepAliveTimeout() { return endpoint.getKeepAliveTimeout(); }
301 public void setKeepAliveTimeout(int timeout) { endpoint.setKeepAliveTimeout(timeout); }
302
303 public boolean getUseSendfile() { return endpoint.getUseSendfile(); }
304 public void setUseSendfile(boolean useSendfile) { /* No sendfile for AJP */ }
305
306 public int getPollTime() { return endpoint.getPollTime(); }
307 public void setPollTime(int pollTime) { endpoint.setPollTime(pollTime); }
308
309 public void setPollerSize(int pollerSize) { endpoint.setPollerSize(pollerSize); }
310 public int getPollerSize() { return endpoint.getPollerSize(); }
311
312 // -------------------------------------- AjpConnectionHandler Inner Class
313
314
315 protected static class AjpConnectionHandler implements Handler {
316
317 protected AjpAprProtocol proto;
318 protected AtomicLong registerCount = new AtomicLong(0);
319 protected RequestGroupInfo global = new RequestGroupInfo();
320
321 protected ConcurrentLinkedQueue<AjpAprProcessor> recycledProcessors =
322 new ConcurrentLinkedQueue<AjpAprProcessor>() {
323 protected AtomicInteger size = new AtomicInteger(0);
324 public boolean offer(AjpAprProcessor processor) {
325 boolean offer = (proto.processorCache == -1) ? true : (size.get() < proto.processorCache);
326 //avoid over growing our cache or add after we have stopped
327 boolean result = false;
328 if ( offer ) {
329 result = super.offer(processor);
330 if ( result ) {
331 size.incrementAndGet();
332 }
333 }
334 if (!result) unregister(processor);
335 return result;
336 }
337
338 public AjpAprProcessor poll() {
339 AjpAprProcessor result = super.poll();
340 if ( result != null ) {
341 size.decrementAndGet();
342 }
343 return result;
344 }
345
346 public void clear() {
347 AjpAprProcessor next = poll();
348 while ( next != null ) {
349 unregister(next);
350 next = poll();
351 }
352 super.clear();
353 size.set(0);
354 }
355 };
356
357 public AjpConnectionHandler(AjpAprProtocol proto) {
358 this.proto = proto;
359 }
360
361 // FIXME: Support for this could be added in AJP as well
362 public SocketState event(long socket, SocketStatus status) {
363 return SocketState.CLOSED;
364 }
365
366 public SocketState process(long socket) {
367 AjpAprProcessor processor = recycledProcessors.poll();
368 try {
369
370 if (processor == null) {
371 processor = createProcessor();
372 }
373
374 if (processor instanceof ActionHook) {
375 ((ActionHook) processor).action(ActionCode.ACTION_START, null);
376 }
377
378 if (processor.process(socket)) {
379 return SocketState.OPEN;
380 } else {
381 return SocketState.CLOSED;
382 }
383
384 } catch(java.net.SocketException e) {
385 // SocketExceptions are normal
386 AjpAprProtocol.log.debug
387 (sm.getString
388 ("ajpprotocol.proto.socketexception.debug"), e);
389 } catch (java.io.IOException e) {
390 // IOExceptions are normal
391 AjpAprProtocol.log.debug
392 (sm.getString
393 ("ajpprotocol.proto.ioexception.debug"), e);
394 }
395 // Future developers: if you discover any other
396 // rare-but-nonfatal exceptions, catch them here, and log as
397 // above.
398 catch (Throwable e) {
399 // any other exception or error is odd. Here we log it
400 // with "ERROR" level, so it will show up even on
401 // less-than-verbose logs.
402 AjpAprProtocol.log.error
403 (sm.getString("ajpprotocol.proto.error"), e);
404 } finally {
405 if (processor instanceof ActionHook) {
406 ((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
407 }
408 recycledProcessors.offer(processor);
409 }
410 return SocketState.CLOSED;
411 }
412
413 protected AjpAprProcessor createProcessor() {
414 AjpAprProcessor processor = new AjpAprProcessor(proto.packetSize, proto.endpoint);
415 processor.setAdapter(proto.adapter);
416 processor.setTomcatAuthentication(proto.tomcatAuthentication);
417 processor.setRequiredSecret(proto.requiredSecret);
418 register(processor);
419 return processor;
420 }
421
422 protected void register(AjpAprProcessor processor) {
423 if (proto.getDomain() != null) {
424 synchronized (this) {
425 try {
426 long count = registerCount.incrementAndGet();
427 RequestInfo rp = processor.getRequest().getRequestProcessor();
428 rp.setGlobalProcessor(global);
429 ObjectName rpName = new ObjectName
430 (proto.getDomain() + ":type=RequestProcessor,worker="
431 + proto.getName() + ",name=AjpRequest" + count);
432 if (log.isDebugEnabled()) {
433 log.debug("Register " + rpName);
434 }
435 Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
436 rp.setRpName(rpName);
437 } catch (Exception e) {
438 log.warn("Error registering request");
439 }
440 }
441 }
442 }
443
444 protected void unregister(AjpAprProcessor processor) {
445 if (proto.getDomain() != null) {
446 synchronized (this) {
447 try {
448 RequestInfo rp = processor.getRequest().getRequestProcessor();
449 rp.setGlobalProcessor(null);
450 ObjectName rpName = rp.getRpName();
451 if (log.isDebugEnabled()) {
452 log.debug("Unregister " + rpName);
453 }
454 Registry.getRegistry(null, null).unregisterComponent(rpName);
455 rp.setRpName(null);
456 } catch (Exception e) {
457 log.warn("Error unregistering request", e);
458 }
459 }
460 }
461 }
462
463 }
464
465
466 // -------------------- Various implementation classes --------------------
467
468
469 protected String domain;
470 protected ObjectName oname;
471 protected MBeanServer mserver;
472
473 public ObjectName getObjectName() {
474 return oname;
475 }
476
477 public String getDomain() {
478 return domain;
479 }
480
481 public ObjectName preRegister(MBeanServer server,
482 ObjectName name) throws Exception {
483 oname=name;
484 mserver=server;
485 domain=name.getDomain();
486 return name;
487 }
488
489 public void postRegister(Boolean registrationDone) {
490 }
491
492 public void preDeregister() throws Exception {
493 }
494
495 public void postDeregister() {
496 }
497
498
499 }